Zum Inhalt springen

Entwicklerhandbuch

Schicke Beiträge aus deiner eigenen Content-Pipeline an Amaposta: Medien hochladen, einen Beitrag für alle Kanäle erstellen, ihn planen und Bescheid bekommen, sobald er live ist.

Authentifizierung

Erstelle im Dashboard unter Einstellungen, API und Webhooks einen API-Schlüssel. Schlüssel beginnen mit ss_live_, gehören zu genau einem Workspace und lassen sich auf bestimmte Projekte beschränken. Schicke den Schlüssel bei jeder Anfrage als Bearer-Token mit:

Schlüssel prüfen
curl https://www.amaposta.com/api/v1/me \
  -H "Authorization: Bearer ss_live_…"

Die Antwort nennt den Schlüssel, seine Berechtigungen und den Workspace. Jeder Schlüssel hat eine oder mehrere Berechtigungen:

BerechtigungErlaubt
content:readProjekte, Kanäle, Beiträge und Medien lesen.
content:writeMedien hochladen und Beiträge erstellen oder bearbeiten.
posts:publishOhne Prüfung planen und veröffentlichen. Ohne diese Berechtigung landen Beiträge in Projekten mit Freigaben in der Freigabe-Inbox und bleiben überall sonst Entwürfe.

Jeder Schlüssel kann 120 Anfragen pro Minute stellen. Bei mehr Anfragen bekommst du 429 mit dem Code rate_limited; warte kurz und versuche es erneut.

Projekte und Kanäle

Beiträge gehören zu einem Projekt. Liste die Projekte auf, die dein Schlüssel sehen kann, und dann die Kanäle eines Projekts. Die ID eines Kanals brauchst du, um gezielt auf ihm zu posten oder seinen Text zu überschreiben.

Projekte und Kanäle auflisten
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"

Ein Kanal hat einen provider (youtube, instagram, tiktok, x, facebook, custom oder sandbox), einen status (active, expiring, needs_reconnect, error oder disabled) und die Beitragsarten, die er unterstützt (supports).

Medien hochladen

Bilder und Videos lädst du einmal pro Projekt hoch und kannst sie dann in beliebig vielen Beiträgen verwenden. Dafür gibt es drei Wege.

Direkter Upload

Schicke die Datei als Multipart-Formulardaten in einem Feld namens file. Am besten für Dateien bis zu einigen hundert 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]"

Presigned-Upload für große Dateien

Fordere eine Upload-URL an, lade die Bytes per PUT direkt in den Speicher und sag Amaposta dann, dass du fertig bist.

Drei Anfragen
# 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"

Von einer URL importieren

Gib Amaposta eine öffentliche URL, dann wird die Datei im Hintergrund heruntergeladen. Du kannst diesen Schritt auch überspringen und beim Erstellen des Beitrags media_url mitgeben.

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

Jeder Upload liefert ein Medienobjekt zurück. Sein status wechselt von pending über processing zu ready (oder zu failed, mit einem error). Nach der Verarbeitung enthält es width, height, duration_ms und ein Vorschaubild. Frage GET /api/v1/media/{id} regelmäßig ab oder warte auf den Webhook media.ready. Die zurückgegebene url ist signiert und 12 Stunden gültig.

Beitrag erstellen

POST /api/v1/projects/{project_id}/posts erstellt einen Beitrag mit einer Zustellung pro Kanal. Amaposta passt ihn an jede Plattform an: Titel gehen nur dorthin, wo es ein Titelfeld gibt, und Hashtags werden auf YouTube zu Tags und anderswo Teil des Texts.

Videobeitrag erstellen und in die Warteschlange stellen
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

Felder

FeldFunktion
kindtext, image oder video. Pflichtfeld.
titleWird von Plattformen mit Titelfeld verwendet: YouTube, TikTok-Fotobeiträge, Facebook-Videos.
descriptionBildunterschrift, Beschreibung oder Beitragstext. Wird auch als body, caption oder text angenommen.
hashtagsEine Liste, mit oder ohne #.
media_id, media_urlDie ID eines hochgeladenen Mediums oder eine öffentliche URL, von der Amaposta die Datei für dich lädt. Nötig für Bild- und Videobeiträge.
cover_media_id, cover_urlEin Titelbild für Videos, sofern die Plattform eines unterstützt.
channelsIDs der Kanäle, auf denen gepostet wird. Lässt du das Feld weg, werden alle aktiven Kanäle des Projekts verwendet, die diese Beitragsart unterstützen.
overridesÄnderungen pro Kanal, mit der Kanal-ID als Schlüssel: title, description, hashtags und plattformspezifische options wie privacy bei YouTube.
schedulemode ist at (mit einem ISO-8601-Zeitpunkt in at), now oder queue für den nächsten freien Sendeplatz. Lässt du das Feld weg, entsteht ein Entwurf.
external_refDeine eigene Referenz; du bekommst sie im Beitrag und in den Zustellungen an Kanäle mit eigener API zurück.
metadataBeliebiges JSON, das mit dem Beitrag gespeichert wird. Mit "pipeline" kennzeichnest du generierte Inhalte; das Dashboard zeigt das an, und Webhooks melden als Herkunft pipeline.

Die Antwort ist der Beitrag, mit einem Eintrag in deliveries pro Kanal.

Sichere Wiederholungen

Im Netz gehen Anfragen verloren. Schicke beim Erstellen eines Beitrags den Header Idempotency-Key mit: Eine Wiederholung mit demselben Schlüssel liefert dann den Beitrag aus dem ersten Versuch, statt einen zweiten anzulegen. Nimm etwas Stabiles aus deinem System, etwa eine Job- oder Episoden-ID. Schlüssel werden auf 200 Zeichen gekürzt.

Planen und veröffentlichen

Mit POST /api/v1/posts/{post_id}/schedule planst du einen Entwurf ein, veröffentlichst ihn sofort, stellst ihn in die Warteschlange oder nimmst ihn wieder aus dem Kalender.

Für einen Zeitpunkt planen
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"}'
  • atVeröffentlicht zum angegebenen Zeitpunkt, bis zu zwei Jahre im Voraus.
  • nowVeröffentlicht sofort.
  • queueNimmt den nächsten freien Sendeplatz der Kanäle des Beitrags innerhalb der nächsten 12 Wochen.
  • unscheduleMacht den Beitrag wieder zum Entwurf.

Bevor etwas geplant wird, laufen dieselben Checks wie im Editor. Schlägt einer fehl, antwortet die Anfrage mit 400 und einer Meldung wie Fix this before scheduling: YouTube needs a title. Zum Planen brauchst du die Berechtigung posts:publish oder ein Projekt mit aktivierten Freigaben; dann wartet der Beitrag in der Freigabe-Inbox.

Beiträge und Zustellungen abrufen

Liste die Beiträge eines Projekts auf, gefiltert nach status (kommagetrennte Status oder inbox, queue bzw. history), einem Zeitfenster from/to sowie limit und offset. Die Antwort ist {"items": […], "total": n}.

Was ansteht, und ein Beitrag im Detail
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"

Ein Beitrag hat einen Gesamtstatus in status (draft, in_review, changes_requested, ready, scheduled, publishing, published, partial oder failed) und eine Zustellung pro Kanal:

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

Zustellungen haben die Status pending, scheduled, publishing, processing, published, failed und cancelled. DELETE /api/v1/posts/{post_id} löscht einen Beitrag.

Fehler

Alle Fehler haben dieselbe Form: ein error-Objekt mit einem stabilen code, einer message, die du Menschen anzeigen kannst, und manchmal fields (Meldungen pro Feld) und einem hint.

422 Unprocessable Entity
{
  "error": {
    "code": "validation",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "kind": "Use text, image or video."
    }
  }
}
StatusCodeBedeutung
400bad_requestDie Anfrage lässt sich so nicht ausführen, etwa wenn ein Beitrag geplant werden soll, der seine Checks nicht besteht.
401unauthorizedDer API-Schlüssel fehlt, ist falsch oder wurde widerrufen.
402plan_limitDer Tarif des Workspaces erlaubt das nicht; limit nennt das betroffene Limit.
403forbiddenDem Schlüssel fehlt eine Berechtigung oder der Zugriff auf das Projekt.
404not_foundProjekt, Beitrag, Medium oder Endpunkt nicht gefunden.
409conflictDie Änderung passt nicht zum aktuellen Zustand, etwa beim Bearbeiten eines Beitrags, der gerade veröffentlicht wird.
422validationEin oder mehrere Felder müssen korrigiert werden; siehe fields.
429rate_limitedMehr als 120 Anfragen pro Minute mit diesem Schlüssel.
502provider_errorEine Plattform hat abgelehnt oder ist ausgefallen; hint sagt, was zu tun ist.

Webhooks

Lege unter Einstellungen, API und Webhooks einen Endpunkt an, wähle die gewünschten Ereignisse und kopiere sein Signatur-Secret. Amaposta schickt für jedes Ereignis einen POST mit JSON an deine URL. Kommt keine 2xx-Antwort oder innerhalb von 15 Sekunden gar keine, versucht Amaposta es mit zunehmendem Abstand erneut. Endpunkte, die dauerhaft fehlschlagen, werden abgeschaltet, und Admins bekommen eine Benachrichtigung.

EreignisGesendet, wenn
post.createdEin Beitrag wurde erstellt (Dashboard oder API)
post.scheduledEin Beitrag wurde geplant
post.approvedEin Beitrag wurde freigegeben
post.publishedEin Beitrag ist auf einem Kanal live gegangen
post.failedEin Beitrag konnte auf einem Kanal nicht veröffentlicht werden
channel.connectedEin Kanal wurde verbunden oder neu verbunden
channel.disconnectedEin Kanal hat seine Verbindung verloren
media.readyEin Upload wurde fertig verarbeitet
media.failedEin Upload konnte nicht verarbeitet werden
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=…"
  }
}

Signatur prüfen

X-Amaposta-Signature hat die Form t=<Unix-Sekunden>,v1=<hex>. Dabei ist v1 gleich HMAC_SHA256(secret, t + "." + body) über den unveränderten Request-Body. Berechne den Wert selbst, vergleiche in konstanter Zeit und lehne alte Zeitstempel ab, um Replay-Angriffe zu verhindern.

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

Kanäle mit eigener API

Ein Kanal mit eigener API veröffentlicht an einen HTTPS-Endpunkt, den du betreibst: dein CMS, ein Newsletter-Tool oder eine Plattform, die Amaposta noch nicht abdeckt. Er steht im Kalender und bekommt Checks, Freigaben und Wiederholungen wie jeder andere Kanal. Geht ein Beitrag raus, schickt Amaposta einen POST pro Zustellung:

Was dein Endpunkt empfängt
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" }
}

So antwortest du

  • 2xxVeröffentlicht. Gib optional {"id": "…", "url": "…"} zurück, dann erscheint der Link am Beitrag.
  • 401, 403Der Schlüssel wurde abgelehnt. Der Kanal wird zum Neuverbinden markiert, und Admins werden benachrichtigt. Verwende diese Codes nicht für eine ungültige Signatur.
  • 5xx, 408, 429Vorübergehend. Amaposta versucht es mit zunehmendem Abstand erneut.
  • Andere 4xxDer Beitrag wird mit deiner Meldung als fehlgeschlagen markiert. Jemand aus dem Team kann ihn korrigieren und auf „Wiederholen“ klicken.
Ein Empfänger 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 });
});

Dein Endpunkt muss aus dem Internet erreichbar sein; Weiterleitungen werden nicht verfolgt. Beim Verbinden des Kanals prüft Amaposta ihn mit einem GET an deine Health-URL, falls du eine angibst, sonst mit einer OPTIONS-Anfrage an den Endpunkt.

Sandbox-Kanäle

Ein Sandbox-Kanal verhält sich wie ein echter, aber nichts verlässt Amaposta. Damit baust und testest du deine Integration von Anfang bis Ende: Beiträge werden geplant, gehen auf Sendung, werden veröffentlicht oder schlagen fehl und lösen dieselben Webhooks aus. Sandbox-Kanäle sind in jedem Tarif kostenlos und zählen nicht zum Kanal-Limit.

Beim Hinzufügen legst du fest, wie er sich verhält:

  • Immer erfolgreichJeder Beitrag wird veröffentlicht.
  • Ab und zu fehlschlagenErlebe Fehler und Wiederholungen, ohne auf einen echten Ausfall zu warten.
  • Immer fehlschlagenTeste, wie du mit post.failed umgehst.
  • Abgelaufene VerbindungSieh dir an, wie Hinweise und Links zum Neuverbinden aussehen, wenn der Zugriff abläuft.

Plattformregeln

Was jeder Kanaltyp akzeptiert. Die Checks verwenden dieselben Limits, daher fallen die meisten Probleme schon vor dem Planen auf und nicht erst beim Veröffentlichen.

Plattform Beitrag Titel Text Hashtags Medien
YouTube Video TitelPflichtfeld, bis zu 100 Zeichen BeschreibungBis zu 5.000 Zeichen Werden als Tags gesendet, bis zu 60 MP4, MOV, WebM, AVI, MKV; bis 256 GB; 1 Sek. bis 12 Std.
Konten ohne bestätigte Telefonnummer können bis zu 15 Minuten hochladen und kein eigenes Titelbild setzen. Jeder Upload verbraucht API-Kontingent; das Standardkontingent von Google reicht für etwa sechs Uploads pro Tag und App.
Instagram Bild Kein Titel BildunterschriftBis zu 2.200 Zeichen Werden an den Text angehängt, bis zu 30 JPEG; bis 8 MB
Video Kein Titel BildunterschriftBis zu 2.200 Zeichen Werden an den Text angehängt, bis zu 30 MP4, MOV; bis 300 MB; 3 Sek. bis 15 Min.
Erfordert ein Instagram-Business- oder Creator-Konto. Instagram erlaubt 50 per API veröffentlichte Beiträge pro Konto innerhalb von 24 Stunden. Reine Textbeiträge sind auf Instagram nicht möglich.
TikTok Bild TitelBis zu 90 Zeichen BeschreibungBis zu 4.000 Zeichen Werden an den Text angehängt JPEG, WebP; bis 20 MB
Video Kein Titel BildunterschriftBis zu 2.200 Zeichen Werden an den Text angehängt MP4, MOV, WebM; bis 4 GB; 3 Sek. bis 10 Min.
Beiträge aus Apps, die TikToks Prüfung noch nicht durchlaufen haben, sieht nur das Konto selbst. TikTok wählt das Titelbild aus einem Videobild; eigene Titelbilder werden nicht unterstützt. Reine Textbeiträge sind auf TikTok nicht möglich.
X Text Kein Titel BeitragPflichtfeld, bis zu 280 (Links zählen als 23, Emojis als 2) Werden an den Text angehängt Keine
Bild Kein Titel BeitragBis zu 280 (Links zählen als 23, Emojis als 2) Werden an den Text angehängt JPEG, PNG, WebP, GIF; bis 5 MB
Video Kein Titel BeitragBis zu 280 (Links zählen als 23, Emojis als 2) Werden an den Text angehängt MP4, MOV; bis 512 MB; 0,5 Sek. bis 2:20 Min.
Posten über die X-API erfordert einen kostenpflichtigen API-Tarif für deine Entwickler-App. Hashtags zählen zur Zeichenbegrenzung.
Facebook Text Kein Titel BeitragPflichtfeld, bis zu 63.206 Zeichen Werden an den Text angehängt Keine
Bild Kein Titel BildunterschriftBis zu 63.206 Zeichen Werden an den Text angehängt JPEG, PNG, GIF, WebP; bis 10 MB
Video TitelBis zu 255 Zeichen BeschreibungBis zu 63.206 Zeichen Werden an den Text angehängt MP4, MOV; bis 1 GB; 1 Sek. bis 20 Min.
Veröffentlicht nur auf Seiten; auf persönliche Profile lässt sich über die API nicht posten.
Eigene API Text TitelOhne Längenbegrenzung BeschreibungOhne Längenbegrenzung Werden als Tags gesendet Keine
Bild TitelOhne Längenbegrenzung BeschreibungOhne Längenbegrenzung Werden als Tags gesendet JPEG, PNG, WebP, GIF
Video TitelOhne Längenbegrenzung BeschreibungOhne Längenbegrenzung Werden als Tags gesendet MP4, MOV, WebM
Jede Anfrage trägt eine HMAC-SHA256-Signatur im Header, damit du prüfen kannst, dass sie von Amaposta kommt. Antworte mit 2xx und optional mit JSON {"id": "…", "url": "…"}. Bei 408, 429 und 5xx wird mit zunehmendem Abstand erneut versucht; 401/403 markieren den Kanal zum Neuverbinden; andere 4xx lassen die Zustellung fehlschlagen, damit du sie korrigieren und erneut versuchen kannst.
Sandbox Text TitelBis zu 200 Zeichen BeschreibungBis zu 5.000 Zeichen Werden an den Text angehängt, bis zu 30 Keine
Bild TitelBis zu 200 Zeichen BeschreibungBis zu 5.000 Zeichen Werden an den Text angehängt, bis zu 30 JPEG, PNG, WebP, GIF
Video TitelBis zu 200 Zeichen BeschreibungBis zu 5.000 Zeichen Werden an den Text angehängt, bis zu 30 MP4, MOV, WebM
Das Veröffentlichen dauert einen Moment, damit du den Statuswechsel live verfolgen kannst.