Your first upload
Walk the real three-step presigned-upload flow — request a slot, PUT the bytes, complete — then read back the public URL.
TODO: translate to Indonesian. English source is authoritative; render it into Indonesian below.
This tutorial walks the complete upload flow: store an image in Berkasna and read back its public URL.
Prerequisites. A Berkasna secret API key (sk_live_…), used from a server. The upload-slot and complete steps reject publishable keys with 403 SECRET_KEY_REQUIRED. See API keys and access.
The public API accepts images only, up to 5 MB. Allowed types are image/png, image/jpeg, image/webp, image/svg+xml, and image/gif. Anything else returns 400 UNSUPPORTED_MEDIA_TYPE; anything larger than 5 MB returns 400 FILE_TOO_LARGE.
Request an upload slot
From your server, with a secret key, ask Berkasna for an upload slot.
curl -X POST \
-H "X-API-Key: sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"filename":"hero.png","mimeType":"image/png","size":523456}' \
"https://api.sawala.cloud/public/berkasna/upload/presigned"The response gives you the assetId, the storage r2Key, and a temporary uploadUrl:
{
"assetId": "ast_abc123",
"r2Key": "acme/originals/ast_abc123/hero.png",
"uploadUrl": "https://...",
"expiresAt": "2026-06-20T10:30:00Z"
}The uploadUrl is valid for about 15 minutes. You can optionally pass folderId in the request body.
PUT the file bytes
Send the raw file bytes to the uploadUrl. Do not include an API key on this request — the URL is already pre-authorized.
async function uploadToBerkasna(file, uploadUrl) {
const res = await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': file.type },
body: file,
});
if (!res.ok) throw new Error(`Upload failed: ${res.status}`);
}Mark the upload complete
Back on your server, with the secret key, tell Berkasna the bytes have landed.
curl -X POST \
-H "X-API-Key: sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"assetId":"ast_abc123"}' \
"https://api.sawala.cloud/public/berkasna/upload/complete"The response confirms the new status only — it does not include a URL:
{ "id": "ast_abc123", "status": "completed" }You can optionally pass fileHash, width, and height in this request.
Read the public URL
To get the public URL, fetch the asset. This endpoint accepts a public key.
curl -H "X-API-Key: pk_live_xxx" \
"https://api.sawala.cloud/public/berkasna/assets/ast_abc123"The asset includes a computed url on the berkasna.sawala.cloud host:
{
"id": "ast_abc123",
"filename": "hero.png",
"mimeType": "image/png",
"status": "completed",
"url": "https://berkasna.sawala.cloud/acme/originals/ast_abc123/hero.png"
}You can also construct the same URL yourself from the r2Key you received in step 1.
Next steps
- Avoid re-uploading: check
/check-duplicate?hash=...before requesting a slot. - See how to upload a public file.
- Read the API reference.