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:
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:
| Permisiune | Permite |
|---|---|
content:read | Citește proiecte, canale, postări și fișiere media. |
content:write | Încarcă fișiere media și creează sau editează postări. |
posts:publish | Programează ș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.
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.
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.
# 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.
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.
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âmp | Ce face |
|---|---|
kind | text, image sau video. Obligatoriu. |
title | Folosit de platforme cu câmp pentru titlu: YouTube, postări foto TikTok, videoclipuri Facebook. |
description | Legenda, descrierea sau textul postării. Acceptat și ca body, caption sau text. |
hashtags | O listă, cu sau fără #. |
media_id, media_url | ID-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_url | O imagine de copertă pentru videoclipuri, dacă platforma permite acest lucru. |
channels | ID-urile canalelor pe care să publici. Omite câmpul pentru a folosi toate canalele active din proiect care acceptă acest tip de postare. |
overrides | Modificări pentru fiecare canal, indexate după ID-ul canalului: title, description, hashtags și options ale platformei, precum privacy pentru YouTube. |
schedule | mode 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_ref | Referința ta proprie, returnată în postare și în livrările API personalizate. |
metadata | JSON 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.
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}.
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": [
{
"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.
{
"error": {
"code": "validation",
"message": "Please fix the highlighted fields.",
"fields": {
"kind": "Use text, image or video."
}
}
}
| Stare | Cod | Semnificație |
|---|---|---|
| 400 | bad_request | Cererea nu poate fi executată așa cum a fost solicitată, de exemplu programarea unei postări care nu trece verificările. |
| 401 | unauthorized | Cheia API lipsește, este greșită sau a fost revocată. |
| 402 | plan_limit | Planul spațiului de lucru nu permite această acțiune; limit indică limita. |
| 403 | forbidden | Cheii îi lipsește o permisiune sau accesul la proiect. |
| 404 | not_found | Nu există un astfel de proiect, postare, fișier media sau punct de acces. |
| 409 | conflict | Modificarea intră în conflict cu starea actuală, de exemplu editarea unei postări aflate deja în curs de publicare. |
| 422 | validation | Unul sau mai multe câmpuri trebuie corectate; vezi fields. |
| 429 | rate_limited | Peste 120 de cereri pe minut cu această cheie. |
| 502 | provider_error | O 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.
| Eveniment | Trimis când |
|---|---|
post.created | O postare a fost creată (din panoul de control sau API) |
post.scheduled | O postare a fost programată |
post.approved | O postare a fost aprobată |
post.published | O postare a fost publicată pe un canal |
post.failed | Publicarea unei postări pe un canal a eșuat |
channel.connected | Un canal a fost conectat sau reconectat |
channel.disconnected | Un canal și-a pierdut conexiunea |
media.ready | Procesarea unui fișier încărcat s-a încheiat |
media.failed | Un fișier încărcat nu a putut fi procesat |
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.
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);
}
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:
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" }
}
Authorization- Cheia ta API. Numele antetului și prefixul (
Bearerimplicit) sunt setate pe canal. Idempotency-Key- ID-ul livrării. Reîncercarea aceleiași livrări trimite aceeași cheie, astfel încât poți omite operațiunile deja efectuate.
X-Amaposta-Delivery- Din nou ID-ul livrării, pentru jurnalele tale.
X-Amaposta-Signature- Semnat ca webhookurile, cu propriul secret de semnare al canalului.
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ă.
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. | |||||
| 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. | |||||
| 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. | |||||