Lewati ke konten

Panduan pengembang

Kirim postingan ke Amaposta dari alur konten Anda sendiri: unggah media, buat postingan untuk semua kanal, jadwalkan, dan terima kabar saat sudah tayang.

Autentikasi

Buat kunci API di dasbor melalui Pengaturan, API dan webhook. Kunci diawali ss_live_, milik satu ruang kerja, dan dapat dibatasi ke proyek tertentu. Kirim kunci sebagai token bearer pada setiap permintaan:

Periksa kunci
curl https://www.amaposta.com/api/v1/me \
  -H "Authorization: Bearer ss_live_…"

Respons menyebutkan nama kunci, cakupan izinnya, dan ruang kerja. Setiap kunci memiliki satu atau beberapa cakupan:

Cakupan izinMengizinkan
content:readBaca proyek, kanal, postingan, dan media.
content:writeUnggah media dan buat atau edit postingan.
posts:publishJadwalkan dan terbitkan tanpa peninjauan. Tanpanya, postingan masuk ke kotak peninjauan di proyek yang memakai persetujuan, dan tetap menjadi draf di proyek lainnya.

Setiap kunci dapat membuat 120 permintaan per menit. Jika terlampaui, Anda menerima 429 dengan kode rate_limited; tunggu sebentar dan coba lagi.

Proyek dan kanal

Postingan berada di proyek. Daftar proyek yang dapat dilihat kunci Anda, lalu kanal dalam salah satunya. Anda memerlukan id kanal untuk menargetkannya atau menimpa teksnya.

Daftar proyek dan kanal
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"

Kanal memiliki provider (youtube, instagram, tiktok, x, facebook, custom atau sandbox), status (active, expiring, needs_reconnect, error atau disabled), serta jenis postingan yang didukung dalam supports.

Unggah media

Gambar dan video diunggah sekali per proyek dan dapat dipakai dalam berapa pun postingan. Ada tiga cara mengunggah.

Unggah langsung

Kirim berkas sebagai data formulir multipart dalam kolom bernama file. Cocok untuk berkas hingga beberapa ratus 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]"

Unggahan bertanda tangan untuk berkas besar

Minta URL unggahan, kirim byte langsung ke penyimpanan dengan PUT, lalu beri tahu Amaposta bahwa Anda sudah selesai.

Tiga permintaan
# 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"

Impor dari URL

Berikan URL publik kepada Amaposta dan berkas akan diunduh di latar belakang. Anda juga dapat melewati langkah ini dan memberikan media_url saat membuat postingan.

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

Setiap unggahan mengembalikan objek media. status-nya berubah dari pending ke processing lalu ready (atau failed, dengan error). Setelah diproses, objek memuat width, height, duration_ms, dan gambar mini. Periksa berkala GET /api/v1/media/{id} atau dengarkan webhook media.ready. url yang dikembalikan bertanda tangan dan berlaku selama 12 jam.

Buat postingan

POST /api/v1/projects/{project_id}/posts membuat satu postingan dengan pengiriman untuk setiap kanal. Amaposta menyesuaikannya per platform: judul hanya dikirim ke platform dengan kolom judul, tagar menjadi tag di YouTube dan teks sebaris di tempat lain.

Buat postingan video dan tambahkan ke antrean
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

Kolom

KolomFungsi
kindtext, image, atau video. Wajib.
titleDipakai platform dengan kolom judul: YouTube, postingan foto TikTok, video Facebook.
descriptionKeterangan, deskripsi, atau teks postingan. Juga diterima sebagai body, caption, atau text.
hashtagsDaftar, dengan atau tanpa #.
media_id, media_urlId media yang diunggah, atau URL publik yang diunduh Amaposta untuk Anda. Diperlukan untuk postingan gambar dan video.
cover_media_id, cover_urlGambar sampul video, jika didukung platform.
channelsId kanal tujuan postingan. Abaikan untuk memakai semua kanal aktif di proyek yang mendukung jenis postingan ini.
overridesPerubahan per kanal dengan id kanal sebagai kunci: title, description, hashtags, dan options platform, seperti privacy YouTube.
schedulemode berupa at (dengan at ISO 8601), now, atau queue untuk slot posting kosong berikutnya. Abaikan untuk membuat draf.
external_refReferensi Anda sendiri, dikembalikan pada postingan dan pengiriman Custom API.
metadataJSON bebas yang disimpan bersama postingan. Sertakan "pipeline" untuk menandai konten yang dihasilkan; dasbor menampilkannya dan webhook melaporkan asalnya sebagai pipeline.

Respons berupa postingan, termasuk satu entri dalam deliveries per kanal.

Percobaan ulang yang aman

Jaringan dapat menggagalkan permintaan. Kirim header Idempotency-Key saat membuat postingan, dan percobaan ulang dengan kunci yang sama mengembalikan postingan dari percobaan pertama alih-alih membuat yang kedua. Gunakan nilai stabil dari pihak Anda, seperti id tugas atau episode. Kunci dipotong menjadi 200 karakter.

Jadwalkan dan terbitkan

Jadwalkan draf, terbitkan sekarang, tambahkan ke antrean, atau hapus dari kalender dengan POST /api/v1/posts/{post_id}/schedule.

Jadwalkan untuk waktu tertentu
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"}'
  • atMenerbitkan pada waktu yang diberikan, hingga dua tahun ke depan.
  • nowLangsung menerbitkan.
  • queueMengambil slot posting kosong berikutnya untuk kanal postingan dalam 12 minggu ke depan.
  • unscheduleMengembalikan postingan ke draf.

Sebelum dijadwalkan, pemeriksaan yang sama dengan penyunting dijalankan. Jika ada yang gagal, permintaan mengembalikan 400 dengan pesan seperti Perbaiki sebelum menjadwalkan: YouTube memerlukan judul. Penjadwalan memerlukan cakupan posts:publish, atau proyek dengan persetujuan aktif, yang membuat postingan menunggu di kotak peninjauan.

Baca postingan dan pengiriman

Daftar postingan proyek, difilter menurut status (status dipisahkan koma, atau inbox, queue, atau history), rentang from/to, limit, dan offset. Respons berupa {"items": […], "total": n}.

Jadwal mendatang, dan detail satu postingan
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"

Postingan memiliki status keseluruhan (draft, in_review, changes_requested, ready, scheduled, publishing, published, partial atau failed) dan satu pengiriman per 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
  }
]

Status pengiriman adalah pending, scheduled, publishing, processing, published, failed, dan cancelled. DELETE /api/v1/posts/{post_id} menghapus postingan.

Kesalahan

Semua kesalahan memiliki struktur yang sama: objek error dengan code stabil, message yang dapat ditampilkan kepada pengguna, dan terkadang fields (pesan per kolom) serta hint.

422 Unprocessable Entity
{
  "error": {
    "code": "validation",
    "message": "Please fix the highlighted fields.",
    "fields": {
      "kind": "Use text, image or video."
    }
  }
}
StatusKodeArti
400bad_requestPermintaan tidak dapat dijalankan sesuai instruksi, seperti menjadwalkan postingan yang gagal pemeriksaan.
401unauthorizedKunci API tidak ada, salah, atau dicabut.
402plan_limitPaket ruang kerja tidak mengizinkannya; limit menyebutkan batasnya.
403forbiddenKunci tidak memiliki cakupan izin atau akses ke proyek.
404not_foundProyek, postingan, media, atau endpoint tidak ditemukan.
409conflictPerubahan bertentangan dengan status saat ini, seperti mengedit postingan yang sedang diterbitkan.
422validationSatu atau beberapa kolom perlu diperbaiki; lihat fields.
429rate_limitedLebih dari 120 permintaan per menit pada kunci ini.
502provider_errorPlatform menolak atau gagal; hint menjelaskan tindakan yang perlu dilakukan.

Webhook

Tambahkan endpoint di Pengaturan, API dan webhook, pilih peristiwa yang diinginkan dan salin rahasia penandatanganannya. Amaposta mengirim JSON melalui POST ke URL Anda untuk setiap peristiwa. Respons selain 2xx, atau tanpa jawaban dalam 15 detik, dicoba ulang dengan jeda bertahap. Endpoint yang terus gagal dinonaktifkan dengan pemberitahuan kepada admin.

PeristiwaDikirim saat
post.createdPostingan dibuat (dasbor atau API)
post.scheduledPostingan dijadwalkan
post.approvedPostingan disetujui
post.publishedPostingan tayang di kanal
post.failedPostingan gagal diterbitkan di kanal
channel.connectedKanal dihubungkan atau dihubungkan ulang
channel.disconnectedKoneksi kanal terputus
media.readyUnggahan selesai diproses
media.failedUnggahan tidak dapat diproses
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=…"
  }
}

Verifikasi tanda tangan

X-Amaposta-Signature adalah t=<unix seconds>,v1=<hex>, dengan v1 berupa HMAC_SHA256(secret, t + "." + body) atas isi permintaan mentah. Hitung sendiri, bandingkan dalam waktu konstan, dan tolak stempel waktu lama untuk mencegah pengulangan.

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

Kanal Custom API

Kanal Custom API menerbitkan ke endpoint HTTPS yang Anda jalankan: CMS, alat buletin, atau platform yang belum didukung Amaposta. Kanal tampil di kalender serta mendapat pemeriksaan, persetujuan, dan percobaan ulang seperti kanal lain. Saat postingan dikirim, Amaposta mengirim satu POST per pengiriman:

Yang diterima endpoint Anda
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" }
}

Cara membalas

  • 2xxDiterbitkan. Opsional, kembalikan {"id": "…", "url": "…"} dan tautan muncul pada postingan.
  • 401, 403Kunci ditolak. Kanal ditandai perlu koneksi ulang dan admin diberi tahu. Jangan gunakan ini untuk tanda tangan yang salah.
  • 5xx, 408, 429Sementara. Amaposta mencoba ulang dengan jeda bertahap.
  • 4xx lainnyaPostingan ditandai gagal beserta pesan Anda, dan pengguna dapat memperbaikinya lalu menekan Coba lagi.
Penerima dalam 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 });
});

Endpoint harus dapat dijangkau dari internet; pengalihan tidak diikuti. Saat menghubungkan kanal, Amaposta memeriksanya melalui GET ke URL kesehatan jika diatur, atau permintaan OPTIONS ke endpoint.

Kanal Sandbox

Kanal sandbox berperilaku seperti kanal asli, tetapi tidak ada yang keluar dari Amaposta. Gunakan untuk membangun dan menguji integrasi menyeluruh: postingan dijadwalkan, tayang, terbit atau gagal, dan memicu webhook yang sama. Kanal Sandbox gratis pada semua paket dan tidak dihitung dalam batas kanal.

Pilih perilakunya saat menambahkan:

  • Selalu berhasilSetiap postingan diterbitkan.
  • Sesekali gagalLihat kegagalan dan percobaan ulang tanpa menunggu gangguan nyata.
  • Selalu gagalUji penanganan post.failed Anda.
  • Koneksi kedaluwarsaLihat tampilan peringatan dan tautan koneksi ulang saat akses berakhir.

Aturan platform

Yang diterima setiap jenis kanal. Pemeriksaan memakai batas yang sama, sehingga sebagian besar masalah muncul sebelum penjadwalan postingan, bukan saat penerbitan.

Platform Postingan Judul Teks Tagar Media
YouTube Video JudulWajib, maksimal 100 karakter DeskripsiMaksimal 5.000 karakter Dikirim sebagai tag, maksimal 60 MP4, MOV, WebM, AVI, MKV; maksimal 256 GB; 1 dtk hingga 12 jam
Akun yang belum memverifikasi nomor telepon dapat mengunggah maksimal 15 menit dan tidak dapat menetapkan gambar mini kustom. Setiap unggahan memakai kuota API; kuota bawaan Google mengizinkan sekitar enam unggahan per hari per aplikasi.
Instagram Gambar Tanpa judul KeteranganMaksimal 2.200 karakter Ditambahkan ke teks, maksimal 30 JPEG; maksimal 8 MB
Video Tanpa judul KeteranganMaksimal 2.200 karakter Ditambahkan ke teks, maksimal 30 MP4, MOV; maksimal 300 MB; 3 dtk hingga 15 min
Memerlukan akun Instagram Business atau Creator. Instagram mengizinkan 50 postingan melalui API per akun dalam rentang 24 jam berjalan. Postingan teks saja tidak dapat dibuat di Instagram.
TikTok Gambar JudulMaksimal 90 karakter DeskripsiMaksimal 4.000 karakter Ditambahkan ke teks JPEG, WebP; maksimal 20 MB
Video Tanpa judul KeteranganMaksimal 2.200 karakter Ditambahkan ke teks MP4, MOV, WebM; maksimal 4 GB; 3 dtk hingga 10 min
Postingan dari aplikasi yang belum menyelesaikan audit TikTok hanya terlihat oleh pembuatnya. TikTok memilih sampul dari bingkai video; gambar sampul kustom tidak didukung. Postingan teks saja tidak dapat dibuat di TikTok.
X Teks Tanpa judul PostinganWajib, maksimal 280 (tautan dihitung 23, emoji 2) Ditambahkan ke teks Tidak ada
Gambar Tanpa judul PostinganMaksimal 280 (tautan dihitung 23, emoji 2) Ditambahkan ke teks JPEG, PNG, WebP, GIF; maksimal 5 MB
Video Tanpa judul PostinganMaksimal 280 (tautan dihitung 23, emoji 2) Ditambahkan ke teks MP4, MOV; maksimal 512 MB; 0,5 dtk hingga 2:20 min
Dengan aplikasi pengembang X sendiri, postingan memakai kredit API sesuai penggunaannya. Tagar dihitung dalam batas karakter.
Facebook Teks Tanpa judul PostinganWajib, maksimal 63.206 karakter Ditambahkan ke teks Tidak ada
Gambar Tanpa judul KeteranganMaksimal 63.206 karakter Ditambahkan ke teks JPEG, PNG, GIF, WebP; maksimal 10 MB
Video JudulMaksimal 255 karakter DeskripsiMaksimal 63.206 karakter Ditambahkan ke teks MP4, MOV; maksimal 1 GB; 1 dtk hingga 20 min
Hanya menerbitkan ke Halaman; API tidak dapat memposting ke profil pribadi.
Custom API Teks JudulTanpa batas panjang DeskripsiTanpa batas panjang Dikirim sebagai tag Tidak ada
Gambar JudulTanpa batas panjang DeskripsiTanpa batas panjang Dikirim sebagai tag JPEG, PNG, WebP, GIF
Video JudulTanpa batas panjang DeskripsiTanpa batas panjang Dikirim sebagai tag MP4, MOV, WebM
Setiap permintaan ditandatangani dengan header HMAC-SHA256 agar dapat diverifikasi berasal dari Amaposta. Balas dengan 2xx dan JSON opsional {"id": "…", "url": "…"}. Respons 408, 429, dan 5xx dicoba ulang dengan jeda bertahap; 401/403 menandai kanal untuk koneksi ulang; 4xx lainnya menggagalkan pengiriman agar dapat diperbaiki dan dicoba ulang.
Sandbox Teks JudulMaksimal 200 karakter DeskripsiMaksimal 5.000 karakter Ditambahkan ke teks, maksimal 30 Tidak ada
Gambar JudulMaksimal 200 karakter DeskripsiMaksimal 5.000 karakter Ditambahkan ke teks, maksimal 30 JPEG, PNG, WebP, GIF
Video JudulMaksimal 200 karakter DeskripsiMaksimal 5.000 karakter Ditambahkan ke teks, maksimal 30 MP4, MOV, WebM
Penerbitan memerlukan waktu sebentar agar Anda dapat melihat perubahan status secara langsung.