Dokumentasi API SuryaDocs
Semua endpoint berada di bawah https://docs.suryacloud.my.id. Contoh ditulis dalam PHP native
dengan cURL — tidak perlu framework, tidak perlu Composer, dan berjalan di shared hosting biasa.
Mulai cepat
- Daftar, lalu buat API key di dashboard.
- Isi saldo (atau pakai kuota gratis playground untuk mencoba).
- Kirim dokumen ke endpoint, simpan
idpekerjaannya. - Tunggu hasilnya — lewat polling atau webhook.
Autentikasi
Setiap permintaan ke /v1/* memerlukan API key rahasia Anda di header
Authorization:
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
pk_live_…).
Key hanya ditampilkan satu kali saat dibuat dan tidak disimpan dalam bentuk yang bisa kami tampilkan ulang. Kehilangan key berarti membuat yang baru dan mencabut yang lama.
Alur asinkron
Semua operasi bersifat asinkron. Permintaan Anda langsung dibalas
202 Accepted berisi id pekerjaan, dan pemrosesan berjalan di
server kami.
Ini disengaja: mengonversi dokumen bisa memakan beberapa detik, dan menahan worker PHP-FPM hosting Anda selama itu adalah cara tercepat membuat seluruh situs ikut melambat saat permintaan menumpuk. Dengan pola ini, hosting Anda hanya mengirim dan menerima — tidak pernah menunggu.
{
"id": "9f1c2e64-...",
"type": "convert",
"status": "queued",
"resultUrl": null,
"createdAt": "2026-08-20T09:12:44.000Z",
"expiresAt": "2026-08-21T09:12:44.000Z"
}
Status berpindah queued → processing → succeeded
atau failed. Ambil hasilnya lewat dua cara:
- Polling — paling sederhana, tidak perlu URL publik.
- Webhook — lebih hemat, kami yang menghubungi server Anda.
Convert — Office ke PDF
POST /v1/convert
Mengubah DOCX, DOC, ODT, XLSX, PPTX, atau RTF menjadi PDF menggunakan LibreOffice.
<?php
// Ubah DOCX menjadi PDF. Permintaan langsung kembali (202) —
// pemrosesan berjalan di server SuryaDocs, bukan di hosting Anda.
$ch = curl_init('https://docs.suryacloud.my.id/v1/convert');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . SURYADOCS_API_KEY],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('/path/surat.docx'),
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
$jobId = $response['id']; // simpan ini
// status: "queued" -> nanti jadi "succeeded"
Lalu tunggu dan unduh hasilnya:
<?php
// Cara paling sederhana: tanya statusnya sampai selesai.
// (Kalau server Anda bisa menerima webhook, itu lebih hemat — lihat bagian Webhook.)
function suryadocsTunggu(string $jobId, int $maksDetik = 60): array {
$mulai = time();
while (time() - $mulai < $maksDetik) {
$ch = curl_init('https://docs.suryacloud.my.id/v1/jobs/' . $jobId);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . SURYADOCS_API_KEY],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($job['status'] === 'succeeded') return $job;
if ($job['status'] === 'failed') throw new RuntimeException($job['error']);
sleep(2);
}
throw new RuntimeException('waktu tunggu habis');
}
$job = suryadocsTunggu($jobId);
// Unduh hasilnya
file_put_contents('/path/surat.pdf', file_get_contents(
$job['resultUrl'] . '?', false,
stream_context_create(['http' => [
'header' => 'Authorization: Bearer ' . SURYADOCS_API_KEY,
]])
));
| Field | Wajib | Keterangan |
|---|---|---|
file | ya* | Berkas dokumen (multipart) |
sourceUrl | ya* | Alternatif file: URL publik dokumen |
*salah satu dari keduanya.
Overlay — kop surat
POST /v1/overlay
Menempelkan gambar (kop surat, logo, stempel) di atas PDF yang sudah ada, presisi di sisi server — bukan bergantung pada pengaturan cetak peramban masing-masing pengguna.
<?php
// Tempel kop surat di atas PDF yang sudah ada.
$ch = curl_init('https://docs.suryacloud.my.id/v1/overlay');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . SURYADOCS_API_KEY],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('/path/surat.pdf'),
'asset' => new CURLFile('/path/kop-surat.png'),
'position' => 'top-center', // lihat tabel posisi di bawah
'pages' => 'all', // all | first | last | "1,3,5"
],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
| Field | Default | Keterangan |
|---|---|---|
file | — | PDF dasar (wajib) |
asset | — | Gambar PNG/JPG (wajib) |
position | top-center | top-left, top-center, top-right, middle-center, bottom-left, bottom-center, bottom-right |
pages | all | all, first, last, atau daftar seperti 1,3,5 |
width | lebar halaman | Lebar dalam poin (1 poin = 1/72 inci) |
opacity | 1 | 0–1 |
x, y | — | Koordinat manual; menimpa position |
Watermark
POST /v1/watermark
Menambahkan teks miring transparan (atau gambar) di seluruh halaman PDF.
<?php
$ch = curl_init('https://docs.suryacloud.my.id/v1/watermark');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . SURYADOCS_API_KEY],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('/path/dokumen.pdf'),
'text' => 'RAHASIA',
'opacity' => '0.15',
'pages' => 'all',
],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
| Field | Default | Keterangan |
|---|---|---|
text | — | Teks watermark (wajib, kecuali memakai asset) |
asset | — | Gambar watermark, alternatif dari text |
opacity | 0.15 | 0–1 |
rotation | 45 | Derajat |
pages | all | Sama seperti overlay |
Tanda tangan
POST /v1/sign
Menempelkan gambar tanda tangan pada posisi tertentu. Parameternya sama persis
dengan overlay, hanya default-nya berbeda:
position=bottom-right, pages=last, width=140.
Template — HTML ke PDF
POST /v1/templates/{id}/render
Simpan template HTML sekali di dashboard, lalu render berkali-kali dengan data
berbeda. Placeholder ditulis {{nama_field}} dan mendukung jalur bertitik
seperti {{invoice.total}}.
<?php
// Simpan template HTML sekali lewat dashboard, lalu render berkali-kali
// dengan data berbeda. Placeholder ditulis {{nama_field}}.
$ch = curl_init('https://docs.suryacloud.my.id/v1/templates/' . $templateId . '/render');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . SURYADOCS_API_KEY,
'Content-Type: application/json',
],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'data' => [
'nama' => 'Budi Santoso',
'nomor' => '001/SD/VIII/2026',
'tanggal' => '20 Agustus 2026',
],
]),
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
Setiap nilai yang disisipkan otomatis di-escape sebagai HTML, sehingga data dari pengguna akhir Anda tidak dapat menyuntikkan markup ke dalam dokumen. Gambar berformat URL di dalam template kami unduh dan sematkan sendiri sebelum dirender.
Webhook
Daftarkan URL penerima di dashboard, dan kami akan mengirim POST berisi
JSON setiap kali pekerjaan selesai — tidak perlu polling.
Setiap pengiriman menyertakan header tanda tangan:
X-Webhook-Signature: sha256=<hmac-sha256 dari body mentah>
json_decode yang di-encode ulang, karena urutan kunci dan
spasi bisa berubah dan tanda tangan tidak akan cocok. Ini kesalahan integrasi paling umum.
PHP
<?php
// terima-webhook.php — daftarkan URL ini di dashboard SuryaDocs.
// PENTING: verifikasi tanda tangan terhadap BODY MENTAH, bukan hasil
// json_decode lalu di-encode ulang (urutan kunci bisa berubah).
$raw = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $raw, SURYADOCS_WEBHOOK_SECRET);
// hash_equals: perbandingan waktu-konstan, bukan ===
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('signature tidak valid');
}
$event = json_decode($raw, true);
if ($event['event'] === 'job.succeeded') {
$jobId = $event['job']['id'];
$resultUrl = $event['job']['resultUrl'];
// ... unduh dan simpan hasilnya
}
http_response_code(200);
echo 'ok';
Node.js
import crypto from 'node:crypto';
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.header('x-webhook-signature') ?? '';
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.SURYADOCS_WEBHOOK_SECRET)
.update(req.body) // Buffer mentah, bukan objek hasil parse
.digest('hex');
const ok = signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!ok) return res.status(401).send('signature tidak valid');
const event = JSON.parse(req.body.toString());
res.sendStatus(200);
});
Python
import hmac, hashlib
@app.post("/webhook")
def webhook():
raw = request.get_data() # bytes mentah
signature = request.headers.get("X-Webhook-Signature", "")
expected = "sha256=" + hmac.new(
SURYADOCS_WEBHOOK_SECRET.encode(), raw, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
return "signature tidak valid", 401
event = request.get_json()
return "ok", 200
Pengiriman yang gagal karena masalah sementara diulang hingga 3 kali (jeda 2 dan 5 detik). Balasan 4xx dianggap permanen dan tidak diulang. Setelah 10 kegagalan berturut-turut, endpoint dinonaktifkan otomatis dan bisa diaktifkan lagi dari dashboard.
Widget embed
Untuk kebutuhan di sisi browser — misalnya membiarkan pengunjung situs Anda mengunggah dan mengonversi dokumen sendiri — tempelkan cuplikan ini. Tidak perlu backend, dan API key rahasia Anda tidak pernah ikut terkirim.
<!-- Tempel di halaman web Anda. Tidak perlu backend sama sekali. -->
<div id="suryadocs-widget"
data-pk="pk_live_xxxxxxxxxxxx"
data-mode="convert"
data-height="420px"></div>
<script src="https://docs.suryacloud.my.id/widget/v1/suryadocs.js" async></script>
| Atribut | Keterangan |
|---|---|
data-pk | Kunci publik (pk_live_…) dari dashboard |
data-mode | convert atau watermark |
data-height | Tinggi widget, contoh 420px |
Kode error
| Kode | Arti |
|---|---|
400 | Permintaan tidak valid — berkas salah format, parameter keliru |
401 | API key tidak ada, salah, atau sudah dicabut |
402 | Saldo tidak mencukupi — isi saldo di dashboard |
403 | Akun ditangguhkan |
404 | Pekerjaan atau template tidak ditemukan |
409 | Hasil diminta sebelum pekerjaan selesai |
410 | Berkas hasil sudah kedaluwarsa dan dihapus |
413 | Berkas melebihi 20 MB |
429 | Terlalu banyak permintaan — lihat header Retry-After |
Setiap respons error menyertakan requestId. Sertakan nilai itu bila
menghubungi dukungan — dengan itu kami bisa menemukan permintaan Anda persis di log.
Batas & retensi
| Hal | Nilai |
|---|---|
| Ukuran berkas maksimal (API) | 20 MB |
| Permintaan per menit | 30 per API key |
| Pekerjaan bersamaan | 5 per akun |
| Retensi berkas hasil | 24 jam |
Berkas yang Anda unggah dihapus segera setelah pekerjaan selesai. Berkas hasil
dihapus otomatis setelah 24 jam — unduh dan simpan di sistem Anda
sendiri. Setelah itu resultUrl akan menjawab 410.