Mergi la conținut

Ghid pentru dezvoltatori

Trimite postări la Amaposta din propriul flux de conținut: încarcă fișiere media, creează o postare pentru fiecare canal, programeaz-o și primește confirmarea când este publicată.

Autentificare

Creează o cheie API în panoul de control, la Setări, API și webhookuri. Cheile încep cu ss_live_, aparțin unui singur spațiu de lucru și pot fi limitate la anumite proiecte. Trimite cheia ca token bearer la fiecare cerere:

Verifică o cheie
curl https://www.amaposta.com/api/v1/me \
  -H "Authorization: Bearer ss_live_…"

Răspunsul identifică cheia, permisiunile sale și spațiul de lucru. Fiecare cheie are una sau mai multe permisiuni:

PermisiunePermite
content:readCitește proiecte, canale, postări și fișiere media.
content:writeÎncarcă fișiere media și creează sau editează postări.
posts:publishProgramează și publică fără verificare. Fără această permisiune, postările ajung în căsuța de verificare în proiectele care folosesc aprobări și rămân ciorne în celelalte.

Fiecare cheie permite 120 de cereri pe minut. Peste această limită primești 429 cu codul rate_limited; așteaptă puțin și reîncearcă.

Proiecte și canale

Postările aparțin unui proiect. Listează proiectele pe care le poate vedea cheia ta, apoi canalele unuia dintre ele. Ai nevoie de ID-ul unui canal pentru a-l selecta sau a-i personaliza textul.

Listează proiecte și canale
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 canal are un provider (youtube, instagram, tiktok, x, facebook, custom sau sandbox), un status (active, expiring, needs_reconnect, error sau disabled) și o listă supports cu tipurile de postări acceptate.

Încarcă fișiere media

Imaginile și videoclipurile sunt încărcate o singură dată pe proiect și pot fi folosite în oricâte postări. Există trei modalități de a le adăuga.

Încărcare directă

Trimite fișierul ca date de formular multipart într-un câmp numit file. Recomandat pentru fișiere de până la câteva sute de 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]"

Încărcare presemnată pentru fișiere mari

Solicită un URL de încărcare, trimite octeții direct în stocare prin PUT, apoi anunță Amaposta că ai terminat.

Trei cereri
# 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"

Importă de la un URL

Oferă Amaposta un URL public și va descărca fișierul în fundal. Poți și să omiți acest pas și să transmiți media_url când creezi postarea.

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"}'

Fiecare încărcare returnează un obiect media. Valoarea status trece din pending în processing și apoi în ready (sau failed, cu un error). După procesare, obiectul conține width, height, duration_ms și o miniatură. Interoghează periodic GET /api/v1/media/{id} sau ascultă webhookul media.ready. url returnat este semnat și valabil 12 ore.

Creează o postare

POST /api/v1/projects/{project_id}/posts creează o postare cu câte o livrare pentru fiecare canal. Amaposta o adaptează pentru fiecare platformă: titlurile apar doar acolo unde există un câmp pentru titlu, iar hashtagurile devin etichete pe YouTube și text încorporat pe celelalte platforme.

Creează o postare video și adaug-o în coadă
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

Câmpuri

CâmpCe face
kindtext, image sau video. Obligatoriu.
titleFolosit de platforme cu câmp pentru titlu: YouTube, postări foto TikTok, videoclipuri Facebook.
descriptionLegenda, descrierea sau textul postării. Acceptat și ca body, caption sau text.
hashtagsO listă, cu sau fără #.
media_id, media_urlID-ul unui fișier media încărcat sau un URL public de unde Amaposta îl descarcă pentru tine. Necesar pentru postările cu imagini și videoclipuri.
cover_media_id, cover_urlO imagine de copertă pentru videoclipuri, dacă platforma permite acest lucru.
channelsID-urile canalelor pe care să publici. Omite câmpul pentru a folosi toate canalele active din proiect care acceptă acest tip de postare.
overridesModificări pentru fiecare canal, indexate după ID-ul canalului: title, description, hashtags și options ale platformei, precum privacy pentru YouTube.
schedulemode este at (cu un at în format ISO 8601), now sau queue pentru următorul interval de publicare liber. Omite-l pentru a crea o ciornă.
external_refReferința ta proprie, returnată în postare și în livrările API personalizate.
metadataJSON cu structură liberă, salvat împreună cu postarea. Include "pipeline" pentru a marca un conținut generat; panoul de control îl afișează, iar webhookurile raportează originea ca pipeline.

Răspunsul este postarea, incluzând câte o intrare în deliveries pentru fiecare canal.

Reîncercări sigure

Rețelele pierd cereri. Trimite un antet Idempotency-Key când creezi o postare, iar o reîncercare cu aceeași cheie returnează postarea de la prima încercare în loc să creeze o a doua. Folosește un identificator stabil din sistemul tău, precum ID-ul unei sarcini sau al unui episod. Cheile sunt trunchiate la 200 de caractere.

Programează și publică

Programează o ciornă, public-o acum, adaug-o în coadă sau scoate-o din calendar cu POST /api/v1/posts/{post_id}/schedule.

Programează pentru o oră
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"}'
  • atPublică la ora indicată, cu până la doi ani în avans.
  • nowPublică imediat.
  • queueOcupă următorul interval de publicare liber pentru canalele postării în următoarele 12 săptămâni.
  • unscheduleMută postarea înapoi la ciorne.

Înainte de orice programare, se execută aceleași verificări ca în editor. Dacă una eșuează, cererea returnează 400 cu un mesaj precum Rezolvă înainte de programare: YouTube necesită un titlu. Programarea necesită permisiunea posts:publish sau un proiect cu aprobări activate, caz în care postarea așteaptă în căsuța de verificare.

Citește postări și livrări

Listează postările unui proiect, filtrate după status (stări separate prin virgulă sau inbox, queue ori history), un interval from/to, limit și offset. Răspunsul este {"items": […], "total": n}.

Ce urmează și o postare în detaliu
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"

O postare are un status general (draft, in_review, changes_requested, ready, scheduled, publishing, published, partial sau failed) și câte o livrare pentru fiecare canal:

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
  }
]

Stările livrării sunt pending, scheduled, publishing, processing, published, failed și cancelled. DELETE /api/v1/posts/{post_id} elimină o postare.

Erori

Fiecare eroare are aceeași structură: un obiect error cu un code stabil, un message pe care îl poți afișa utilizatorilor și uneori fields (mesaje pentru fiecare câmp) și un hint.

422 Unprocessable Entity
{
  "error": {
    "code": "validation",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "kind": "Use text, image or video."
    }
  }
}
StareCodSemnificație
400bad_requestCererea nu poate fi executată așa cum a fost solicitată, de exemplu programarea unei postări care nu trece verificările.
401unauthorizedCheia API lipsește, este greșită sau a fost revocată.
402plan_limitPlanul spațiului de lucru nu permite această acțiune; limit indică limita.
403forbiddenCheii îi lipsește o permisiune sau accesul la proiect.
404not_foundNu există un astfel de proiect, postare, fișier media sau punct de acces.
409conflictModificarea intră în conflict cu starea actuală, de exemplu editarea unei postări aflate deja în curs de publicare.
422validationUnul sau mai multe câmpuri trebuie corectate; vezi fields.
429rate_limitedPeste 120 de cereri pe minut cu această cheie.
502provider_errorO platformă a refuzat sau a eșuat; hint arată ce trebuie făcut.

Webhookuri

Adaugă un punct de acces în Setări, API și webhookuri, alege evenimentele dorite și copiază secretul de semnare. Amaposta trimite JSON prin POST la URL-ul tău pentru fiecare eveniment. Orice răspuns diferit de 2xx sau lipsa unui răspuns în 15 secunde declanșează reîncercări la intervale crescătoare, iar punctele de acces care eșuează în continuare sunt dezactivate, cu o notificare pentru administratori.

EvenimentTrimis când
post.createdO postare a fost creată (din panoul de control sau API)
post.scheduledO postare a fost programată
post.approvedO postare a fost aprobată
post.publishedO postare a fost publicată pe un canal
post.failedPublicarea unei postări pe un canal a eșuat
channel.connectedUn canal a fost conectat sau reconectat
channel.disconnectedUn canal și-a pierdut conexiunea
media.readyProcesarea unui fișier încărcat s-a încheiat
media.failedUn fișier încărcat nu a putut fi procesat
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=…"
  }
}

Verifică semnătura

X-Amaposta-Signature este t=<unix seconds>,v1=<hex>, unde v1 este HMAC_SHA256(secret, t + "." + body) calculat pe corpul brut al cererii. Calculează-l, compară-l în timp constant și respinge marcajele temporale vechi pentru a preveni reutilizarea cererilor.

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", ""))

Canale API personalizate

Un canal API personalizat publică la un punct de acces HTTPS administrat de tine: CMS-ul tău, un instrument pentru buletine informative sau o platformă pe care Amaposta încă nu o acoperă. Apare în calendar și beneficiază de verificări, aprobări și reîncercări ca orice alt canal. La publicarea unei postări, Amaposta trimite câte un POST pentru fiecare livrare:

Ce primește punctul tău de acces
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" }
}

Cum să răspunzi

  • 2xxPublicată. Poți returna opțional {"id": "…", "url": "…"}, iar linkul apare la postare.
  • 401, 403Cheia a fost respinsă. Canalul este marcat pentru reconectare și administratorii sunt anunțați. Nu folosi aceste coduri pentru o semnătură incorectă.
  • 5xx, 408, 429Temporar. Amaposta reîncearcă la intervale crescătoare.
  • Alte coduri 4xxPostarea este marcată ca eșuată cu mesajul tău, iar cineva poate corecta problema și apăsa Reîncearcă.
Un receptor în 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 });
});

Punctul tău de acces trebuie să fie accesibil de pe internet; redirecționările nu sunt urmate. Când conectezi canalul, Amaposta îl verifică printr-un GET la URL-ul de verificare a funcționării, dacă ai setat unul, sau printr-o cerere OPTIONS la punctul de acces.

Canale Sandbox

Un canal sandbox se comportă ca unul real, dar nimic nu părăsește Amaposta. Folosește-l pentru a crea și testa integrarea de la un capăt la altul: postările sunt programate, intră în emisie, sunt publicate sau eșuează și declanșează aceleași webhookuri. Canalele sandbox sunt gratuite în toate planurile și nu sunt incluse în limitele de canale.

Alege cum se comportă când îl adaugi:

  • Reușește întotdeaunaFiecare postare este publicată.
  • Eșuează din când în cândVezi eșecurile și reîncercările fără să aștepți o întrerupere reală.
  • Eșuează întotdeaunaTestează gestionarea evenimentului post.failed.
  • Conexiune expiratăVezi cum arată alertele și linkurile de reconectare când accesul expiră.

Regulile platformei

Ce acceptă fiecare tip de canal. Verificările folosesc aceleași limite, astfel încât majoritatea problemelor apar înainte de programare, nu la publicare.

Platformă Postare Titlu Text Hashtaguri Media
YouTube Video TitluObligatoriu, limită de caractere: 100 DescriereLimită de caractere: 5.000 Trimise ca etichete, până la 60 MP4, MOV, WebM, AVI, MKV; până la 256 GB; de la 1 s la 12 h
Conturile care nu au verificat un număr de telefon pot încărca până la 15 minute și nu pot seta miniaturi personalizate. Fiecare încărcare consumă din cota API; cota Google implicită permite aproximativ șase încărcări pe zi pentru fiecare aplicație.
Instagram Imagine Fără titlu LegendăLimită de caractere: 2.200 Adăugate în text, până la 30 JPEG; până la 8 MB
Video Fără titlu LegendăLimită de caractere: 2.200 Adăugate în text, până la 30 MP4, MOV; până la 300 MB; de la 3 s la 15 min
Necesită un cont Instagram Business sau Creator. Instagram permite 50 de postări publicate prin API pentru fiecare cont într-un interval continuu de 24 de ore. Postările doar cu text nu sunt posibile pe Instagram.
TikTok Imagine TitluLimită de caractere: 90 DescriereLimită de caractere: 4.000 Adăugate în text JPEG, WebP; până la 20 MB
Video Fără titlu LegendăLimită de caractere: 2.200 Adăugate în text MP4, MOV, WebM; până la 4 GB; de la 3 s la 10 min
Postările din aplicații care nu au finalizat auditul TikTok sunt vizibile doar pentru creator. TikTok alege coperta dintr-un cadru video; imaginile de copertă personalizate nu sunt acceptate. Postările doar cu text nu sunt posibile pe TikTok.
X Text Fără titlu PostareObligatoriu, până la 280 (linkurile se numără ca 23, emojiurile ca 2) Adăugate în text Niciuna
Imagine Fără titlu PostarePână la 280 (linkurile se numără ca 23, emojiurile ca 2) Adăugate în text JPEG, PNG, WebP, GIF; până la 5 MB
Video Fără titlu PostarePână la 280 (linkurile se numără ca 23, emojiurile ca 2) Adăugate în text MP4, MOV; până la 512 MB; de la 0,5 s la 2:20 min
Dacă folosești propria aplicație de dezvoltator X, postările consumă creditele sale API, plătite în funcție de utilizare. Hashtagurile sunt incluse în limita de caractere.
Facebook Text Fără titlu PostareObligatoriu, limită de caractere: 63.206 Adăugate în text Niciuna
Imagine Fără titlu LegendăLimită de caractere: 63.206 Adăugate în text JPEG, PNG, GIF, WebP; până la 10 MB
Video TitluLimită de caractere: 255 DescriereLimită de caractere: 63.206 Adăugate în text MP4, MOV; până la 1 GB; de la 1 s la 20 min
Publică doar pe pagini; API-ul nu permite publicarea pe profiluri personale.
API personalizat Text TitluFără limită de lungime DescriereFără limită de lungime Trimise ca etichete Niciuna
Imagine TitluFără limită de lungime DescriereFără limită de lungime Trimise ca etichete JPEG, PNG, WebP, GIF
Video TitluFără limită de lungime DescriereFără limită de lungime Trimise ca etichete MP4, MOV, WebM
Fiecare cerere este semnată cu un antet HMAC-SHA256 ca să poți verifica dacă provine de la Amaposta. Răspunde cu 2xx și, opțional, JSON {"id": "…", "url": "…"}. Răspunsurile 408, 429 și 5xx sunt reîncercate la intervale crescătoare; 401/403 marchează canalul pentru reconectare; alte coduri 4xx duc la eșecul livrării, ca să poți corecta problema și reîncerca.
Sandbox Text TitluLimită de caractere: 200 DescriereLimită de caractere: 5.000 Adăugate în text, până la 30 Niciuna
Imagine TitluLimită de caractere: 200 DescriereLimită de caractere: 5.000 Adăugate în text, până la 30 JPEG, PNG, WebP, GIF
Video TitluLimită de caractere: 200 DescriereLimită de caractere: 5.000 Adăugate în text, până la 30 MP4, MOV, WebM
Publicarea durează puțin, pentru a putea urmări schimbarea stării în timp real.