Vai al contenuto

Guida per sviluppatori

Invia post ad Amaposta dalla tua pipeline di contenuti: carica contenuti multimediali, crea un post per ogni canale, programmalo e ricevi una notifica quando è online.

Autenticazione

Crea una chiave API nel pannello, in Impostazioni, API e webhook. Le chiavi iniziano con ss_live_, appartengono a uno spazio di lavoro e possono essere limitate a determinati progetti. Invia la chiave come token bearer in ogni richiesta:

Verifica una chiave
curl https://www.amaposta.com/api/v1/me \
  -H "Authorization: Bearer ss_live_…"

La risposta indica il nome della chiave, i suoi ambiti e lo spazio di lavoro. Ogni chiave ha uno o più ambiti:

AmbitoConsente
content:readLeggi progetti, canali, post e contenuti multimediali.
content:writeCarica contenuti multimediali e crea o modifica post.
posts:publishProgramma e pubblica senza revisione. Senza questa autorizzazione, i post passano nella casella di revisione nei progetti che usano le approvazioni e rimangono bozze in tutti gli altri.

Ogni chiave può effettuare 120 richieste al minuto. Oltre questo limite ricevi 429 con il codice rate_limited; attendi un momento e riprova.

Progetti e canali

I post appartengono a un progetto. Elenca i progetti visibili alla tua chiave, poi i canali di uno di essi. Ti servirà l'ID di un canale per sceglierlo come destinazione o personalizzarne il testo.

Elenca progetti e canali
curl https://www.amaposta.com/api/v1/projects \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/channels \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

Un canale ha un provider (youtube, instagram, tiktok, x, facebook, custom o sandbox), uno status (active, expiring, needs_reconnect, error o disabled) e i tipi di post che supports.

Carica contenuti multimediali

Immagini e video vengono caricati una sola volta per progetto e possono essere usati in qualsiasi numero di post. Ci sono tre modalità di caricamento.

Caricamento diretto

Invia il file come dati multipart di un modulo in un campo chiamato file. Ideale per file fino a qualche centinaio di MB.

POST /api/v1/projects/{project_id}/media
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -F "[email protected]"

Caricamento prefirmato per file di grandi dimensioni

Richiedi un URL di caricamento, invia i byte direttamente all'archiviazione con PUT, poi comunica ad Amaposta che hai terminato.

Tre richieste
# 1. Ask for an upload URL. The response is {"media": { … }, "upload_url": "…"}
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/uploads \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filename": "batch-142.mp4", "content_type": "video/mp4", "size_bytes": 48213844}'

# 2. Upload the bytes to upload_url
curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: video/mp4" \
  --upload-file batch-142.mp4

# 3. Finish the upload
curl -X POST https://www.amaposta.com/api/v1/media/$MEDIA_ID/complete \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

Importa da un URL

Fornisci ad Amaposta un URL pubblico e scaricherà il file in background. Puoi anche saltare questo passaggio e passare media_url quando crei il post.

POST /api/v1/projects/{project_id}/media/import
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/media/import \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://cdn.example.com/batch-142.mp4"}'

Ogni caricamento restituisce un oggetto multimediale. Il suo status passa da pending a processing e poi a ready (o failed, con un error). Una volta elaborato contiene width, height, duration_ms e una miniatura. Interroga periodicamente GET /api/v1/media/{id} o ascolta il webhook media.ready. L'url restituito è firmato e valido per 12 ore.

Crea un post

POST /api/v1/projects/{project_id}/posts crea un post con una pubblicazione per ogni canale. Amaposta lo adatta a ogni piattaforma: i titoli vanno solo dove esiste il relativo campo, gli hashtag diventano tag su YouTube e testo in linea altrove.

Crea un post video e aggiungilo alla coda
curl https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: episode-142" \
  -d @- <<'JSON'
{
  "kind": "video",
  "title": "How we roast a batch in 12 minutes",
  "description": "From green beans to first crack. We filmed one batch start to finish.",
  "hashtags": ["coffee", "behindthescenes"],
  "media_url": "https://cdn.example.com/batch-142.mp4",
  "cover_url": "https://cdn.example.com/batch-142-cover.jpg",
  "overrides": {
    "<x-channel-id>": {
      "description": "Batch 142 is out: green beans to first crack in 12 minutes."
    },
    "<youtube-channel-id>": {
      "options": { "privacy": "unlisted" }
    }
  },
  "schedule": { "mode": "queue" },
  "external_ref": "episode-142",
  "metadata": {
    "pipeline": { "name": "nightly-cut", "run": "2026-10-17" }
  }
}
JSON

Campi

CampoCosa fa
kindtext, image o video. Obbligatorio.
titleUsato dalle piattaforme con un campo titolo: YouTube, post fotografici TikTok, video Facebook.
descriptionLa didascalia, la descrizione o il testo del post. Accettato anche come body, caption o text.
hashtagsUn elenco, con o senza #.
media_id, media_urlL'ID di un contenuto multimediale caricato, oppure un URL pubblico da cui Amaposta lo scarica per te. Necessario per i post con immagini e video.
cover_media_id, cover_urlUn'immagine di copertina per i video, dove la piattaforma la supporta.
channelsID dei canali su cui pubblicare. Omettilo per usare tutti i canali attivi del progetto che supportano questo tipo di post.
overridesModifiche per canale indicizzate per ID canale: title, description, hashtags e options della piattaforma, ad esempio privacy di YouTube.
schedulemode è at (con un valore at ISO 8601), now o queue per la prossima fascia di pubblicazione libera. Omettilo per creare una bozza.
external_refUn tuo riferimento, riportato nel post e nelle pubblicazioni API personalizzata.
metadataJSON a struttura libera salvato con il post. Includi "pipeline" per contrassegnare i contenuti generati; il pannello lo mostra e i webhook riportano l'origine come pipeline.

La risposta è il post, con una voce in deliveries per canale.

Nuovi tentativi sicuri

Le reti possono perdere richieste. Invia un header Idempotency-Key quando crei un post: un nuovo tentativo con la stessa chiave restituisce il post del primo tentativo invece di crearne un secondo. Usa un valore stabile dal tuo sistema, ad esempio l'ID di un'attività o di un episodio. Le chiavi vengono troncate a 200 caratteri.

Programma e pubblica

Programma una bozza, pubblicala ora, aggiungila alla coda o rimuovila dal calendario con POST /api/v1/posts/{post_id}/schedule.

Programma per un orario
curl https://www.amaposta.com/api/v1/posts/$POST_ID/schedule \
  -H "Authorization: Bearer $AMAPOSTA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode": "at", "at": "2026-10-20T07:00:00Z"}'
  • atPubblica all'orario indicato, fino a due anni in anticipo.
  • nowPubblica subito.
  • queueUsa la prossima fascia di pubblicazione libera per i canali del post nelle prossime 12 settimane.
  • unscheduleRiporta il post tra le bozze.

Prima di programmare qualsiasi post, vengono eseguiti gli stessi controlli dell'editor. Se uno fallisce, la richiesta restituisce 400 con un messaggio come Risolvi prima di programmare: YouTube richiede un titolo. Per programmare occorre l'ambito posts:publish oppure un progetto con le approvazioni attive; in quest'ultimo caso il post attende nella casella di revisione.

Leggi post e pubblicazioni

Elenca i post di un progetto, filtrati per status (stati separati da virgole, oppure inbox, queue o history), intervallo from/to, limit e offset. La risposta è {"items": […], "total": n}.

Cosa c'è in programma e un post nel dettaglio
curl "https://www.amaposta.com/api/v1/projects/$PROJECT_ID/posts?status=queue" \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

curl https://www.amaposta.com/api/v1/posts/$POST_ID \
  -H "Authorization: Bearer $AMAPOSTA_KEY"

Un post ha uno status complessivo (draft, in_review, changes_requested, ready, scheduled, publishing, published, partial o failed) e una pubblicazione per canale:

deliveries[]
"deliveries": [
  {
    "id": "0192b7d4-…",
    "channel_id": "0192b7a1-…",
    "status": "published",
    "url": "https://www.youtube.com/watch?v=…",
    "error": null,
    "published_at": "2026-10-20T07:00:41Z"
  },
  {
    "id": "0192b7d4-…",
    "channel_id": "0192b7a9-…",
    "status": "failed",
    "url": null,
    "error": {
      "code": "endpoint_unavailable",
      "message": "Your endpoint returned 503 Service Unavailable",
      "hint": "Amaposta will retry with backoff."
    },
    "published_at": null
  }
]

Gli stati di pubblicazione sono pending, scheduled, publishing, processing, published, failed e cancelled. DELETE /api/v1/posts/{post_id} rimuove un post.

Errori

Ogni errore ha la stessa struttura: un oggetto error con un code stabile, un message da mostrare alle persone e, talvolta, fields (messaggi per campo) e un hint.

422 Unprocessable Entity
{
  "error": {
    "code": "validation",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "kind": "Use text, image or video."
    }
  }
}
StatoCodiceSignificato
400bad_requestLa richiesta non può essere eseguita così com'è, ad esempio programmare un post che non supera i controlli.
401unauthorizedLa chiave API è mancante, errata o revocata.
402plan_limitIl piano dello spazio di lavoro non lo consente; limit indica quale limite.
403forbiddenAlla chiave manca un ambito o l'accesso al progetto.
404not_foundProgetto, post, contenuto multimediale o endpoint inesistente.
409conflictLa modifica è incompatibile con lo stato attuale, ad esempio la modifica di un post già in pubblicazione.
422validationUno o più campi richiedono correzioni; consulta fields.
429rate_limitedPiù di 120 richieste al minuto su questa chiave.
502provider_errorUna piattaforma ha rifiutato la richiesta o ha riscontrato un errore; hint indica cosa fare.

Webhook

Aggiungi un endpoint in Impostazioni, API e webhook, scegli gli eventi desiderati e copia il relativo segreto di firma. Amaposta invia JSON al tuo URL tramite POST per ogni evento. Qualsiasi risposta diversa da 2xx, o l'assenza di risposta entro 15 secondi, attiva nuovi tentativi a intervalli crescenti. Gli endpoint che continuano a fallire vengono disattivati e gli amministratori ricevono un avviso.

EventoInviato quando
post.createdUn post è stato creato (pannello o API)
post.scheduledUn post è stato programmato
post.approvedUn post è stato approvato
post.publishedUn post è andato online su un canale
post.failedUn post non è stato pubblicato su un canale
channel.connectedUn canale è stato collegato o ricollegato
channel.disconnectedUn canale ha perso la connessione
media.readyL'elaborazione di un file caricato è terminata
media.failedNon è stato possibile elaborare un file caricato
post.published
POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: Amaposta-Webhooks/1.0
X-Amaposta-Event: post.published
X-Amaposta-Event-Id: 0192b7e0-…
X-Amaposta-Signature: t=1792479641,v1=5f2b0c…

{
  "id": "0192b7e0-…",
  "type": "post.published",
  "created_at": "2026-10-20T07:00:41Z",
  "project_id": "0192a3f1-…",
  "data": {
    "post_id": "0192b7c8-…",
    "delivery_id": "0192b7d4-…",
    "channel_id": "0192b7a1-…",
    "provider": "youtube",
    "external_id": "…",
    "url": "https://www.youtube.com/watch?v=…"
  }
}

Verifica la firma

X-Amaposta-Signature è t=<unix seconds>,v1=<hex>, dove v1 è HMAC_SHA256(secret, t + "." + body) calcolato sul corpo grezzo della richiesta. Calcolalo, confrontalo in tempo costante e rifiuta i timestamp obsoleti per impedire attacchi di replay.

Node.js
import crypto from "node:crypto";

// rawBody: the request body exactly as received (string or Buffer).
export function verifySignature(rawBody, header, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest();
  const given = Buffer.from(parts.v1 ?? "", "hex");
  return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}
Python
import hashlib
import hmac
import time


def verify_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    t = int(parts.get("t", "0"))
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Canali API personalizzata

Un canale API personalizzata pubblica su un endpoint HTTPS gestito da te: il tuo CMS, uno strumento per newsletter o una piattaforma non ancora supportata da Amaposta. Compare nel calendario e riceve controlli, approvazioni e nuovi tentativi come qualsiasi altro canale. Quando un post viene pubblicato, Amaposta invia un POST per ogni pubblicazione:

Cosa riceve il tuo endpoint
POST /amaposta/publish HTTP/1.1
Authorization: Bearer <your API key>
Content-Type: application/json
Idempotency-Key: 0192b7d4-…
X-Amaposta-Delivery: 0192b7d4-…
X-Amaposta-Signature: t=1792479600,v1=9ac41e…

{
  "id": "0192b7d4-…",
  "type": "post.publish",
  "created_at": "2026-10-20T07:00:00Z",
  "content": {
    "kind": "video",
    "title": "How we roast a batch in 12 minutes",
    "text": "From green beans to first crack. We filmed one batch start to finish.",
    "hashtags": ["coffee", "behindthescenes"],
    "media": {
      "url": "https://…",
      "content_type": "video/mp4",
      "size_bytes": 48213844,
      "width": 1080,
      "height": 1920,
      "duration_ms": 58000,
      "filename": "batch-142.mp4"
    },
    "cover": null,
    "options": {}
  },
  "post": {
    "id": "0192b7c8-…",
    "external_ref": "episode-142",
    "scheduled_at": "2026-10-20T07:00:00Z",
    "metadata": {
      "pipeline": { "name": "nightly-cut" }
    }
  },
  "project": { "id": "0192a3f1-…", "name": "Spring launch" },
  "channel": { "id": "0192b7a9-…", "name": "api.example.com" }
}

Come rispondere

  • 2xxPubblicato. Puoi restituire {"id": "…", "url": "…"} per mostrare il link sul post.
  • 401, 403La chiave è stata rifiutata. Il canale viene contrassegnato come da ricollegare e gli amministratori vengono avvisati. Non usare questi stati per una firma errata.
  • 5xx, 408, 429Temporaneo. Amaposta riprova a intervalli crescenti.
  • Altri 4xxIl post viene contrassegnato come non riuscito con il tuo messaggio e qualcuno può correggerlo e premere Riprova.
Un ricevitore in Express
app.post("/amaposta/publish", express.raw({ type: "application/json" }), async (req, res) => {
  const signature = req.get("X-Amaposta-Signature") ?? "";
  if (!verifySignature(req.body, signature, process.env.AMAPOSTA_SIGNING_SECRET)) {
    return res.status(400).json({ message: "Bad signature" });
  }
  const delivery = JSON.parse(req.body);
  const item = await publishToYourSystem(delivery.content, req.get("Idempotency-Key"));
  res.json({ id: item.id, url: item.url });
});

Il tuo endpoint deve essere raggiungibile da Internet; i reindirizzamenti non vengono seguiti. Quando colleghi il canale, Amaposta lo verifica con GET all'URL di controllo dello stato, se impostato, oppure con una richiesta OPTIONS all'endpoint.

Canali sandbox

Un canale sandbox si comporta come uno reale, ma nulla lascia Amaposta. Usalo per sviluppare e testare la tua integrazione dall'inizio alla fine: i post vengono programmati, vanno online, vengono pubblicati o falliscono e attivano gli stessi webhook. I canali sandbox sono gratuiti in ogni piano e non incidono sui limiti dei canali.

Scegli come si comporta quando lo aggiungi:

  • Riesce sempreOgni post viene pubblicato.
  • Fallisce di tanto in tantoOsserva errori e nuovi tentativi senza attendere un'interruzione reale.
  • Fallisce sempreVerifica la tua gestione di post.failed.
  • Connessione scadutaScopri come appaiono gli avvisi e i link di riconnessione quando l'accesso scade.

Regole delle piattaforme

Cosa accetta ogni tipo di canale. I controlli usano gli stessi limiti, quindi la maggior parte dei problemi emerge prima che un post venga programmato anziché al momento della pubblicazione.

Piattaforma Post Titolo Testo Hashtag Contenuti multimediali
YouTube Video TitoloObbligatorio, fino a 100 caratteri DescrizioneFino a 5000 caratteri Inviati come tag, fino a 60 MP4, MOV, WebM, AVI, MKV; fino a 256 GB; da 1 s a 12 h
Gli account che non hanno verificato un numero di telefono possono caricare video fino a 15 minuti e non possono impostare miniature personalizzate. Ogni caricamento consuma quota API; la quota predefinita di Google consente circa sei caricamenti al giorno per app.
Instagram Immagine Senza titolo DidascaliaFino a 2200 caratteri Aggiunti al testo, fino a 30 JPEG; fino a 8 MB
Video Senza titolo DidascaliaFino a 2200 caratteri Aggiunti al testo, fino a 30 MP4, MOV; fino a 300 MB; da 3 s a 15 min
Richiede un account Instagram Business o Creator. Instagram consente 50 post pubblicati tramite API per account nell'arco di 24 ore mobili. Su Instagram non sono possibili post di solo testo.
TikTok Immagine TitoloFino a 90 caratteri DescrizioneFino a 4000 caratteri Aggiunti al testo JPEG, WebP; fino a 20 MB
Video Senza titolo DidascaliaFino a 2200 caratteri Aggiunti al testo MP4, MOV, WebM; fino a 4 GB; da 3 s a 10 min
I post delle app che non hanno completato la verifica di TikTok sono visibili solo al creatore. TikTok sceglie la copertina da un fotogramma del video; le immagini di copertina personalizzate non sono supportate. Su TikTok non sono possibili post di solo testo.
X Testo Senza titolo PostObbligatorio, fino a 280 (i link contano 23, le emoji 2) Aggiunti al testo Nessuno
Immagine Senza titolo PostFino a 280 (i link contano 23, le emoji 2) Aggiunti al testo JPEG, PNG, WebP, GIF; fino a 5 MB
Video Senza titolo PostFino a 280 (i link contano 23, le emoji 2) Aggiunti al testo MP4, MOV; fino a 512 MB; da 0,5 s a 2:20 min
Con la tua app sviluppatore X, i post usano i suoi crediti API a consumo. Gli hashtag rientrano nel limite di caratteri.
Facebook Testo Senza titolo PostObbligatorio, fino a 63.206 caratteri Aggiunti al testo Nessuno
Immagine Senza titolo DidascaliaFino a 63.206 caratteri Aggiunti al testo JPEG, PNG, GIF, WebP; fino a 10 MB
Video TitoloFino a 255 caratteri DescrizioneFino a 63.206 caratteri Aggiunti al testo MP4, MOV; fino a 1 GB; da 1 s a 20 min
Pubblica solo sulle Pagine; non è possibile pubblicare sui profili personali tramite API.
API personalizzata Testo TitoloNessun limite di lunghezza DescrizioneNessun limite di lunghezza Inviati come tag Nessuno
Immagine TitoloNessun limite di lunghezza DescrizioneNessun limite di lunghezza Inviati come tag JPEG, PNG, WebP, GIF
Video TitoloNessun limite di lunghezza DescrizioneNessun limite di lunghezza Inviati come tag MP4, MOV, WebM
Ogni richiesta è firmata con un header HMAC-SHA256, così puoi verificare che provenga da Amaposta. Rispondi con 2xx e, facoltativamente, JSON {"id": "…", "url": "…"}. Le risposte 408, 429 e 5xx vengono ritentate a intervalli crescenti; 401/403 contrassegnano il canale come da ricollegare; gli altri 4xx fanno fallire la pubblicazione, così puoi correggerla e riprovare.
Sandbox Testo TitoloFino a 200 caratteri DescrizioneFino a 5000 caratteri Aggiunti al testo, fino a 30 Nessuno
Immagine TitoloFino a 200 caratteri DescrizioneFino a 5000 caratteri Aggiunti al testo, fino a 30 JPEG, PNG, WebP, GIF
Video TitoloFino a 200 caratteri DescrizioneFino a 5000 caratteri Aggiunti al testo, fino a 30 MP4, MOV, WebM
La pubblicazione richiede un momento, così puoi vedere lo stato cambiare in tempo reale.