SDSuryaDocs

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

  1. Daftar, lalu buat API key di dashboard.
  2. Isi saldo (atau pakai kuota gratis playground untuk mencoba).
  3. Kirim dokumen ke endpoint, simpan id pekerjaannya.
  4. Tunggu hasilnya — lewat polling atau webhook.

Autentikasi

Setiap permintaan ke /v1/* memerlukan API key rahasia Anda di header Authorization:

Authorization: Bearer sk_live_xxxxxxxxxxxxxxxx
Jaga kerahasiaannya. API key rahasia hanya boleh dipakai dari server Anda. Jangan pernah menaruhnya di HTML, JavaScript, atau aplikasi mobile — siapa pun yang melihatnya bisa memakai saldo Anda. Untuk kebutuhan di sisi browser, gunakan widget dengan kunci publik (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, XLSX, XLS, PPTX, PPT, ODT, ODS, ODP, atau RTF menjadi PDF menggunakan LibreOffice — format modern (OOXML) maupun format lama (OLE2) sama-sama didukung.

<?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,
    ]])
));
FieldWajibKeterangan
fileya*Berkas dokumen (multipart)
sourceUrlya*Alternatif file: URL publik dokumen

*salah satu dari keduanya.

Arah sebaliknya (PDF → DOCX) tidak kami sediakan. Hasil konversinya tidak dapat diandalkan untuk dokumen yang sedikit saja kompleks, dan kami memilih tidak menjanjikan sesuatu yang akan mengecewakan di produksi.

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);
FieldDefaultKeterangan
file—PDF dasar (wajib)
asset—Gambar PNG/JPG (wajib)
positiontop-centertop-left, top-center, top-right, middle-center, bottom-left, bottom-center, bottom-right
pagesallall, first, last, atau daftar seperti 1,3,5
widthlebar halamanLebar dalam poin (1 poin = 1/72 inci)
opacity10–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);
FieldDefaultKeterangan
text—Teks watermark (wajib, kecuali memakai asset)
asset—Gambar watermark, alternatif dari text
opacity0.150–1
rotation45Derajat
pagesallSama 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.

Ini adalah stempel visual, bukan tanda tangan elektronik tersertifikasi. Endpoint ini menempelkan gambar ke atas halaman PDF. Ia tidak membuat tanda tangan kriptografis (PAdES) dan tidak memiliki kekuatan hukum sebagai tanda tangan elektronik tersertifikasi. Untuk itu diperlukan penyelenggara sertifikasi elektronik (PSrE) terdaftar Kominfo — di luar cakupan layanan ini untuk saat ini.

Edit teks PDF

POST /v1/edit-text

Menutup teks lama dengan kotak berwarna (putih secara default), lalu menulis teks baru pada posisi yang sama. Bisa mengubah banyak posisi sekaligus dalam satu permintaan — kirim edits sebagai array JSON.

<?php
// "Edit" di sini artinya menutup teks lama dengan kotak, lalu menulis
// teks baru di posisi yang sama -- PDF tidak menyimpan teks sebagai
// paragraf yang bisa disunting langsung. Koordinat x/y dari kiri-bawah
// halaman, dalam satuan poin PDF (1pt = 1/72 inci). Berfungsi sama saja
// pada PDF yang sudah ditandatangani -- stempel tanda tangan cuma konten
// tambahan di halaman, bukan status khusus yang diproteksi.
$edits = [
    ['page' => 1, 'x' => 72, 'y' => 700, 'width' => 200, 'height' => 20, 'text' => 'Budi Santoso'],
    ['page' => 1, 'x' => 72, 'y' => 670, 'width' => 150, 'height' => 20, 'text' => '20 Agustus 2026'],
];

$ch = curl_init('https://docs.suryacloud.my.id/v1/edit-text');
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'),
        'edits' => json_encode($edits),
    ],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
FieldWajibKeterangan
pageYaNomor halaman, mulai dari 1
x, yYaSudut kiri-bawah kotak, dalam poin PDF
width, heightYaUkuran kotak penutup, dalam poin PDF
textYaTeks baru yang ditulis di kotak itu
fontSizeTidakDefault menyesuaikan tinggi kotak
color, boxColorTidakObjek {"r":0,"g":0,"b":0}, nilai 0-1
alignTidakleft (default) atau center
Ini bukan penyuntingan teks PDF yang sesungguhnya, dan bukan redaksi. PDF menyimpan teks sebagai instruksi gambar per-karakter di posisi tertentu, bukan paragraf yang bisa disunting langsung — tidak ada library yang benar-benar bisa "mengganti" teks di tempat. Operasi ini menutup area lama lalu menggambar teks baru di atasnya secara visual. Teks lama tetap ada di dalam berkas dan bisa diekstrak lewat pilih-salin atau alat ekstraksi teks — jangan pakai ini untuk menyembunyikan data sensitif (nomor rekening, NIK, dsb). Cocok untuk memperbaiki nilai pada posisi yang sudah diketahui (nama, tanggal, nomor), kurang cocok untuk paragraf panjang yang perlu reflow.

Gabung PDF

POST /v1/merge

Menggabungkan 2 hingga 20 berkas PDF menjadi satu, persis dalam urutan berkas itu dikirim dalam permintaan. Halaman disalin langsung di level objek PDF (bukan dirender ulang), jadi kualitas dan tautan internal tetap utuh.

<?php
// Gabungkan beberapa PDF jadi satu, dalam urutan field dikirim.
// Nama field bebas — kirim files[0], files[1], ... (idiom cURL PHP untuk
// banyak berkas di satu field) atau nama apa pun; server menerima SEMUA
// berkas dalam satu permintaan, apa pun nama fieldnya.
$berkas = ['/path/sampul.pdf', '/path/isi.pdf', '/path/lampiran.pdf'];
$fields = [];
foreach ($berkas as $i => $path) {
    $fields["files[$i]"] = new CURLFile($path);
}

$ch = curl_init('https://docs.suryacloud.my.id/v1/merge');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . SURYADOCS_API_KEY],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $fields,
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);

Nama field bebas — kirim files[0], files[1] (idiom cURL PHP), atau nama lain apa pun. Yang menentukan urutan hasil gabungan adalah urutan berkas dalam body permintaan, bukan nama fieldnya.

Pisah PDF

POST /v1/split

Kebalikan dari gabung: mengambil sebagian halaman dari satu PDF menjadi PDF baru yang lebih kecil. Sama seperti merge, halaman disalin langsung di level objek PDF (bukan dirender ulang).

<?php
// Ambil halaman 5 sampai 12 dari sebuah PDF, jadi berkas baru yang lebih kecil.
$ch = curl_init('https://docs.suryacloud.my.id/v1/split');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . SURYADOCS_API_KEY],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => [
        'file'  => new CURLFile('/path/dokumen-lengkap.pdf'),
        'pages' => '5-12',   // atau daftar eksplisit: "1,3,5"
    ],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
FieldWajibKeterangan
fileYaPDF sumber
pagesYafirst, last, daftar seperti 1,3,5, atau rentang seperti 2-4 — tidak ada default, karena inti operasi ini justru memilih sebagiannya

Rentang (2-4) dan daftar (1,3,5) bisa dicampur dalam satu nilai, misalnya 1,3,5-8. Berlaku juga untuk parameter pages di overlay dan watermark.

Thumbnail — PDF ke gambar

POST /v1/thumbnail

Merender satu halaman PDF menjadi gambar PNG — untuk pratinjau daftar dokumen tanpa membuka PDF penuh di sisi klien.

<?php
// Pratinjau visual halaman pertama, tanpa membuka PDF penuh — cocok untuk
// daftar dokumen di dashboard Anda sendiri.
$ch = curl_init('https://docs.suryacloud.my.id/v1/thumbnail');
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'),
        'page' => '1',   // opsional, default 1
    ],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
// Setelah "succeeded", resultUrl mengarah ke berkas PNG, bukan PDF.
FieldWajibKeterangan
fileYaPDF sumber
pageTidakNomor halaman, mulai dari 1 (default: 1)
Satu-satunya operasi di API ini yang hasilnya bukan PDF — resultUrl mengarah ke berkas .png, dan tipe kontennya image/png, bukan application/pdf seperti operasi lain.

OCR — jadikan bisa dicari

POST /v1/ocr

Menambahkan lapisan teks tak terlihat ke PDF atau gambar hasil scan, sehingga isinya bisa dicari dan disalin — tampilan halaman tidak berubah sama sekali. Halaman yang sudah punya teks (bukan hasil scan) dilewati apa adanya.

<?php
// Hasil scan (PDF atau gambar tunggal) -> PDF dengan lapisan teks
// tersembunyi yang bisa dicari dan disalin. Tampilan halaman tidak berubah.
$ch = curl_init('https://docs.suryacloud.my.id/v1/ocr');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . SURYADOCS_API_KEY],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => [
        'file' => new CURLFile('/path/hasil-scan.pdf'),
        'lang' => 'ind+eng',   // opsional, default ind+eng
    ],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
FieldWajibKeterangan
fileYaPDF atau gambar (PNG/JPG) hasil scan
langTidakind, eng, ind+eng (default), atau eng+ind
Lebih lambat dari operasi lain. OCR memproses tiap halaman satu per satu dan bisa memakan puluhan detik untuk dokumen panjang — rencanakan lewat webhook daripada polling ketat. Akurasi bergantung kualitas hasil scan (resolusi, kemiringan, kontras); ini bukan pengganti pemeriksaan ulang untuk dokumen yang harus 100% akurat.

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>
Selalu verifikasi tanda tangan. Tanpa verifikasi, siapa pun yang tahu URL penerima Anda bisa mengirim data palsu. Hitung HMAC terhadap body mentah — bukan hasil 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>
AtributKeterangan
data-pkKunci publik (pk_live_…) dari dashboard
data-modeconvert atau watermark
data-heightTinggi widget, contoh 420px
Kunci publik memang terlihat di kode halaman Anda — itu wajar, sama seperti kunci publik penyedia pembayaran. Ia hanya bisa membuat pekerjaan atas nama akun Anda dan membaca hasil pekerjaannya sendiri; ia tidak bisa mengakses data akun, kunci lain, atau pekerjaan yang Anda kirim lewat API rahasia. Batasi domain yang boleh memakainya di dashboard, dan pantau pemakaiannya di sana.

Kode error

KodeArti
400Permintaan tidak valid — berkas salah format, parameter keliru
401API key tidak ada, salah, atau sudah dicabut
402Saldo tidak mencukupi — isi saldo di dashboard
403Akun ditangguhkan
404Pekerjaan atau template tidak ditemukan
409Hasil diminta sebelum pekerjaan selesai
410Berkas hasil sudah kedaluwarsa dan dihapus
413Berkas melebihi 20 MB
429Terlalu 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

HalNilai
Ukuran berkas maksimal (API)20 MB
Permintaan per menit30 per API key
Pekerjaan bersamaan5 per akun
Retensi berkas hasil24 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.