Naar inhoud gaan

Ontwikkelaarshandleiding

Stuur berichten naar Amaposta vanuit je eigen contentpipeline: upload media, maak een bericht voor elk kanaal, plan het in en ontvang een melding wanneer het live is.

Authenticatie

Maak een API-sleutel in het dashboard onder Instellingen, API en webhooks. Sleutels beginnen met ss_live_, horen bij één werkruimte en kunnen tot bepaalde projecten worden beperkt. Stuur de sleutel als bearer-token mee bij elke aanvraag:

Een sleutel controleren
curl https://www.amaposta.com/api/v1/me \
  -H "Authorization: Bearer ss_live_…"

Het antwoord vermeldt de sleutel, de bevoegdheden en de werkruimte. Elke sleutel heeft één of meer bevoegdheden:

BevoegdheidStaat toe
content:readProjecten, kanalen, berichten en media lezen.
content:writeMedia uploaden en berichten maken of bewerken.
posts:publishInplannen en publiceren zonder beoordeling. Zonder deze bevoegdheid gaan berichten in projecten met goedkeuringen naar de beoordelingsinbox en blijven ze elders concepten.

Elke sleutel kan 120 aanvragen per minuut doen. Daarna ontvang je 429 met de code rate_limited; wacht even en probeer het opnieuw.

Projecten en kanalen

Berichten horen bij een project. Vraag de projecten op die je sleutel kan zien en daarna de kanalen in een daarvan. Je hebt de ID van een kanaal nodig om het te selecteren of de tekst ervan aan te passen.

Projecten en kanalen opvragen
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"

Een kanaal heeft een provider (youtube, instagram, tiktok, x, facebook, custom of sandbox), een status (active, expiring, needs_reconnect, error of disabled) en berichttypen die het supports.

Media uploaden

Afbeeldingen en video's worden één keer per project geüpload en kunnen in onbeperkt veel berichten worden gebruikt. Er zijn drie manieren om ze toe te voegen.

Direct uploaden

Stuur het bestand als multipart-formuliergegevens in een veld met de naam file. Het meest geschikt voor bestanden tot enkele honderden 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]"

Vooraf ondertekende upload voor grote bestanden

Vraag een upload-URL aan, stuur de bytes met PUT rechtstreeks naar de opslag en laat Amaposta daarna weten dat je klaar bent.

Drie aanvragen
# 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"

Vanaf een URL importeren

Geef Amaposta een openbare URL en het bestand wordt op de achtergrond gedownload. Je kunt deze stap ook overslaan en media_url meegeven wanneer je het bericht maakt.

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

Elke upload geeft een mediaobject terug. De status gaat van pending naar processing naar ready (of failed, met een error). Na verwerking bevat het width, height, duration_ms en een miniatuur. Poll GET /api/v1/media/{id} of luister naar de webhook media.ready. De teruggegeven url is ondertekend en 12 uur geldig.

Een bericht maken

POST /api/v1/projects/{project_id}/posts maakt één bericht met een plaatsing voor elk kanaal. Amaposta past dit per platform aan: titels gaan alleen naar platforms met een titelveld, hashtags worden tags op YouTube en tekst in het bericht op andere platforms.

Een videobericht maken en in de wachtrij zetten
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

Velden

VeldWat het doet
kindtext, image of video. Verplicht.
titleGebruikt door platforms met een titelveld: YouTube, TikTok-fotoberichten, Facebook-video's.
descriptionHet bijschrift, de beschrijving of de berichttekst. Ook geaccepteerd als body, caption of text.
hashtagsEen lijst, met of zonder #.
media_id, media_urlDe ID van geüploade media of een openbare URL waarvan Amaposta het bestand voor je downloadt. Nodig voor afbeeldings- en videoberichten.
cover_media_id, cover_urlEen omslagafbeelding voor video's, als het platform die ondersteunt.
channelsKanaal-ID's om op te publiceren. Laat dit weg om elk actief kanaal in het project te gebruiken dat dit berichttype ondersteunt.
overridesAanpassingen per kanaal met de kanaal-ID als sleutel: title, description, hashtags en platformspecifieke options, zoals privacy van YouTube.
schedulemode is at (met een ISO 8601-waarde voor at), now of queue voor het volgende vrije publicatiemoment. Laat dit weg om een concept te maken.
external_refJe eigen referentie, die wordt teruggegeven bij het bericht en in Custom API-plaatsingen.
metadataVrije JSON die bij het bericht wordt opgeslagen. Voeg "pipeline" toe om gegenereerde content te markeren; het dashboard toont dit en webhooks vermelden de herkomst als pipeline.

Het antwoord is het bericht, inclusief één vermelding in deliveries per kanaal.

Veilig opnieuw proberen

Netwerken verliezen aanvragen. Stuur een Idempotency-Key-header mee bij het maken van een bericht. Een nieuwe poging met dezelfde sleutel geeft het bericht van de eerste poging terug, in plaats van een tweede te maken. Gebruik een stabiele waarde van jouw kant, zoals een taak- of afleverings-ID. Sleutels worden afgekapt op 200 tekens.

Inplannen en publiceren

Plan een concept in, publiceer het nu, zet het in de wachtrij of haal het van de kalender met POST /api/v1/posts/{post_id}/schedule.

Voor een tijdstip inplannen
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"}'
  • atPubliceert op het opgegeven tijdstip, tot twee jaar vooruit.
  • nowPubliceert direct.
  • queueGebruikt het volgende vrije publicatiemoment voor de kanalen van het bericht in de komende 12 weken.
  • unscheduleZet het bericht terug naar concepten.

Voordat iets wordt ingepland, worden dezelfde controles als in de editor uitgevoerd. Als er één mislukt, geeft de aanvraag 400 terug met een melding zoals Los dit op voordat je inplant: YouTube vereist een titel. Inplannen vereist de bevoegdheid posts:publish of een project met ingeschakelde goedkeuringen; in dat geval wacht het bericht in de beoordelingsinbox.

Berichten en plaatsingen lezen

Vraag de berichten van een project op, gefilterd op status (kommagescheiden statussen of inbox, queue of history), een periode from/to, limit en offset. Het antwoord is {"items": […], "total": n}.

Wat er aankomt en één bericht in 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"

Een bericht heeft een algemene status (draft, in_review, changes_requested, ready, scheduled, publishing, published, partial of failed) en één plaatsing per kanaal:

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

Plaatsingsstatussen zijn pending, scheduled, publishing, processing, published, failed en cancelled. DELETE /api/v1/posts/{post_id} verwijdert een bericht.

Fouten

Elke fout heeft dezelfde structuur: een error-object met een vaste code, een message die je aan mensen kunt tonen en soms fields (meldingen per veld) en een hint.

422 Unprocessable Entity
{
  "error": {
    "code": "validation",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "kind": "Use text, image or video."
    }
  }
}
StatusCodeBetekenis
400bad_requestDe aanvraag kan niet zoals gevraagd worden uitgevoerd, bijvoorbeeld bij het inplannen van een bericht dat niet door de controles komt.
401unauthorizedDe API-sleutel ontbreekt, is onjuist of is ingetrokken.
402plan_limitHet abonnement van de werkruimte staat dit niet toe; limit geeft aan welke limiet geldt.
403forbiddenDe sleutel mist een bevoegdheid of toegang tot het project.
404not_foundGeen dergelijk project, bericht, mediabestand of eindpunt.
409conflictDe wijziging is in strijd met de huidige status, bijvoorbeeld het bewerken van een bericht dat al wordt gepubliceerd.
422validationEen of meer velden moeten worden aangepast; zie fields.
429rate_limitedMeer dan 120 aanvragen per minuut met deze sleutel.
502provider_errorEen platform heeft geweigerd of is vastgelopen; hint geeft aan wat je kunt doen.

Webhooks

Voeg een eindpunt toe bij Instellingen, API en webhooks, kies de gewenste gebeurtenissen en kopieer het ondertekeningsgeheim. Amaposta stuurt bij elke gebeurtenis JSON via POST naar je URL. Bij een antwoord buiten 2xx of zonder antwoord binnen 15 seconden volgt een nieuwe poging met oplopende wachttijd. Eindpunten die blijven mislukken worden uitgeschakeld, met een melding aan beheerders.

GebeurtenisVerstuurd wanneer
post.createdEen bericht is gemaakt (dashboard of API)
post.scheduledEen bericht is ingepland
post.approvedEen bericht is goedgekeurd
post.publishedEen bericht is op een kanaal gepubliceerd
post.failedEen bericht kon niet op een kanaal worden gepubliceerd
channel.connectedEen kanaal is verbonden of opnieuw verbonden
channel.disconnectedEen kanaal heeft de verbinding verloren
media.readyEen upload is verwerkt
media.failedEen upload kon niet worden verwerkt
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=…"
  }
}

Handtekening controleren

X-Amaposta-Signature is t=<unix seconds>,v1=<hex>, waarbij v1 gelijk is aan HMAC_SHA256(secret, t + "." + body) over de onbewerkte aanvraagbody. Bereken dit zelf, vergelijk in constante tijd en weiger oude tijdstempels om herhaling te voorkomen.

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

Custom API-kanalen

Een Custom API-kanaal publiceert naar een HTTPS-eindpunt dat je zelf beheert: je CMS, een nieuwsbriefdienst of een platform dat Amaposta nog niet ondersteunt. Het staat op de kalender en krijgt controles, goedkeuringen en nieuwe pogingen zoals elk ander kanaal. Bij publicatie stuurt Amaposta één POST per plaatsing:

Wat je eindpunt ontvangt
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" }
}

Hoe je antwoordt

  • 2xxGepubliceerd. Geef eventueel {"id": "…", "url": "…"} terug en de link verschijnt bij het bericht.
  • 401, 403De sleutel is geweigerd. Het kanaal wordt gemarkeerd voor opnieuw verbinden en beheerders worden geïnformeerd. Gebruik deze niet voor een ongeldige handtekening.
  • 5xx, 408, 429Tijdelijk. Amaposta probeert het opnieuw met oplopende wachttijd.
  • Overige 4xxHet bericht wordt met je melding als mislukt gemarkeerd, waarna iemand het kan aanpassen en op Opnieuw proberen kan drukken.
Een ontvanger 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 });
});

Je eindpunt moet bereikbaar zijn vanaf het internet; omleidingen worden niet gevolgd. Bij het verbinden van het kanaal controleert Amaposta het met een GET naar je controle-URL als je die hebt ingesteld, of met een OPTIONS-aanvraag naar het eindpunt.

Sandboxkanalen

Een sandboxkanaal gedraagt zich als een echt kanaal, maar niets verlaat Amaposta. Bouw en test hiermee je volledige integratie: berichten worden ingepland, gaan live, worden gepubliceerd of mislukken en activeren dezelfde webhooks. Sandboxkanalen zijn gratis bij elk abonnement en tellen niet mee voor kanaallimieten.

Kies hoe het zich gedraagt wanneer je het toevoegt:

  • Altijd slagenElk bericht wordt gepubliceerd.
  • Af en toe mislukkenBekijk fouten en nieuwe pogingen zonder op een echte storing te wachten.
  • Altijd mislukkenTest je afhandeling van post.failed.
  • Verlopen verbindingBekijk hoe meldingen en links voor opnieuw verbinden eruitzien wanneer de toegang verloopt.

Platformregels

Wat elk kanaaltype accepteert. De controles gebruiken dezelfde limieten, zodat de meeste problemen vóór het inplannen zichtbaar worden, in plaats van bij het publiceren.

Platform Bericht Titel Tekst Hashtags Media
YouTube Video TitelVerplicht, maximaal 100 tekens BeschrijvingMaximaal 5.000 tekens Als tags verstuurd, maximaal 60 MP4, MOV, WebM, AVI, MKV; maximaal 256 GB; 1 s tot 12 u
Accounts zonder geverifieerd telefoonnummer kunnen video's van maximaal 15 minuten uploaden en geen eigen miniaturen instellen. Elke upload verbruikt API-quotum; het standaardquotum van Google staat ongeveer zes uploads per dag per app toe.
Instagram Afbeelding Geen titel BijschriftMaximaal 2.200 tekens Aan de tekst toegevoegd, maximaal 30 JPEG; maximaal 8 MB
Video Geen titel BijschriftMaximaal 2.200 tekens Aan de tekst toegevoegd, maximaal 30 MP4, MOV; maximaal 300 MB; 3 s tot 15 min
Vereist een Instagram Business- of Creator-account. Instagram staat 50 via de API gepubliceerde berichten per account toe in elke voortschrijdende periode van 24 uur. Berichten met alleen tekst zijn niet mogelijk op Instagram.
TikTok Afbeelding TitelMaximaal 90 tekens BeschrijvingMaximaal 4.000 tekens Aan de tekst toegevoegd JPEG, WebP; maximaal 20 MB
Video Geen titel BijschriftMaximaal 2.200 tekens Aan de tekst toegevoegd MP4, MOV, WebM; maximaal 4 GB; 3 s tot 10 min
Berichten van apps die de audit van TikTok niet hebben afgerond, zijn alleen zichtbaar voor de maker. TikTok kiest de omslag uit een videoframe; eigen omslagafbeeldingen worden niet ondersteund. Berichten met alleen tekst zijn niet mogelijk op TikTok.
X Tekst Geen titel BerichtVerplicht, maximaal 280 (links tellen als 23, emoji als 2) Aan de tekst toegevoegd Geen
Afbeelding Geen titel BerichtMaximaal 280 (links tellen als 23, emoji als 2) Aan de tekst toegevoegd JPEG, PNG, WebP, GIF; maximaal 5 MB
Video Geen titel BerichtMaximaal 280 (links tellen als 23, emoji als 2) Aan de tekst toegevoegd MP4, MOV; maximaal 512 MB; 0,5 s tot 2:20 min
Met je eigen X-ontwikkelaarsapp verbruiken berichten de API-credits van die app. Je betaalt naar gebruik. Hashtags tellen mee voor de tekenlimiet.
Facebook Tekst Geen titel BerichtVerplicht, maximaal 63.206 tekens Aan de tekst toegevoegd Geen
Afbeelding Geen titel BijschriftMaximaal 63.206 tekens Aan de tekst toegevoegd JPEG, PNG, GIF, WebP; maximaal 10 MB
Video TitelMaximaal 255 tekens BeschrijvingMaximaal 63.206 tekens Aan de tekst toegevoegd MP4, MOV; maximaal 1 GB; 1 s tot 20 min
Publiceert alleen op pagina's; publiceren op persoonlijke profielen via de API is niet mogelijk.
Custom API Tekst TitelGeen lengtelimiet BeschrijvingGeen lengtelimiet Als tags verstuurd Geen
Afbeelding TitelGeen lengtelimiet BeschrijvingGeen lengtelimiet Als tags verstuurd JPEG, PNG, WebP, GIF
Video TitelGeen lengtelimiet BeschrijvingGeen lengtelimiet Als tags verstuurd MP4, MOV, WebM
Elke aanvraag is ondertekend met een HMAC-SHA256-header, zodat je kunt controleren of deze van Amaposta komt. Antwoord met 2xx en eventueel JSON {"id": "…", "url": "…"}. Bij 408, 429 en 5xx volgt een nieuwe poging met oplopende wachttijd; 401/403 markeren het kanaal voor opnieuw verbinden; andere 4xx laten de plaatsing mislukken zodat je het kunt oplossen en opnieuw proberen.
Sandbox Tekst TitelMaximaal 200 tekens BeschrijvingMaximaal 5.000 tekens Aan de tekst toegevoegd, maximaal 30 Geen
Afbeelding TitelMaximaal 200 tekens BeschrijvingMaximaal 5.000 tekens Aan de tekst toegevoegd, maximaal 30 JPEG, PNG, WebP, GIF
Video TitelMaximaal 200 tekens BeschrijvingMaximaal 5.000 tekens Aan de tekst toegevoegd, maximaal 30 MP4, MOV, WebM
Publiceren duurt even zodat je de status live kunt zien veranderen.