Sawala CloudSawala Cloud — Docs
BerkasnaReference

Berkasna API Reference

Berkasna's public REST API — presigned upload, completion, duplicate check, listing, and asset metadata.

TODO: translate to Indonesian. English source is authoritative; render it into Indonesian below.

Base URL

https://api.sawala.cloud/public/berkasna

Authentication

Send your API key with every request:

X-API-Key: <pk_live_… or sk_live_…>

Authorization: Bearer <key> is also accepted. The presigned-upload and complete endpoints require a secret key; a publishable key there returns 403 SECRET_KEY_REQUIRED. Listing, get-asset, and duplicate-check accept a public key. See API keys and access.

Endpoints

Upload

MethodPathAuthDescription
POST/upload/presignedSecretRequest an upload slot (temporary upload URL)
POST/upload/completeSecretMark an upload complete
GET/check-duplicate?hash={sha256}PublicCheck whether an asset with this hash already exists

Assets

MethodPathAuthDescription
GET/assets?limit=&cursor=&mimeCategory=PublicList assets (paginated)
GET/assets/{id}PublicFetch one asset with metadata, including its public url

POST /upload/presigned

Request body:

{ "filename": "hero.png", "mimeType": "image/png", "size": 523456 }

Optional field: folderId.

Response:

{
  "assetId":   "ast_abc123",
  "r2Key":     "acme/originals/ast_abc123/hero.png",
  "uploadUrl": "https://...",
  "expiresAt": "2026-06-20T10:30:00Z"
}

uploadUrl is a temporary URL valid for about 15 minutes (900 seconds). PUT the raw file bytes to it with no API key. The public URL of a completed public asset is https://berkasna.sawala.cloud/<r2Key>.

POST /upload/complete

Request body:

{ "assetId": "ast_abc123" }

Optional fields: fileHash, width, height.

Response (status only — no url):

{ "id": "ast_abc123", "status": "completed" }

To read the public URL, call GET /assets/{id}.

GET /check-duplicate

GET /check-duplicate?hash=<sha256>

Response:

{ "isDuplicate": true, "assetId": "ast_abc123" }

There is no URL in this response. To get the URL for an existing asset, call GET /assets/{assetId}.

Asset model

{
  "id":        "ast_abc123",
  "filename":  "hero.png",
  "mimeType":  "image/png",
  "size":      523456,
  "status":    "completed",
  "url":       "https://berkasna.sawala.cloud/acme/originals/ast_abc123/hero.png",
  "title":     null,
  "alt":       null,
  "tags":      [],
  "width":     1920,
  "height":    1080,
  "createdAt": "2026-06-20T10:25:00Z"
}

An asset's status is pending while bytes are in flight and completed once the upload is marked complete. The public url is present for public, completed assets; it follows the pattern https://berkasna.sawala.cloud/<org-slug>/originals/<assetId>/<filename>.

Listing query parameters

ParamValuesDescription
limitinteger (default 25, max 100)Assets per page
cursorstringCursor for the next page
mimeCategoryMIME-type prefix, e.g. image, video, applicationFilter by MIME-type prefix

mimeCategory is a prefix match — it matches assets whose MIME type starts with <value>/ (for example image matches image/png and image/webp). Use the actual MIME prefix; values like pdf or document will not match (the underlying types are application/pdf, etc.).

Limits (public API)

  • Accepted types: image/png, image/jpeg, image/webp, image/svg+xml, image/gif
  • Max file size: 5 MB

These image-only / 5 MB limits apply to the public API. Other formats and larger files are handled through the dashboard, not this surface.

Error codes

Errors are returned as a flat object:

{ "error": "CODE" }
HTTPerrorCause
400(validation)Invalid request body
400UNSUPPORTED_MEDIA_TYPEMIME type not in the allowed image list
400FILE_TOO_LARGEFile exceeds 5 MB
401Missing or invalid API key
403SECRET_KEY_REQUIREDPublishable key used on /upload/presigned or /upload/complete
404ASSET_NOT_FOUNDNo asset with that id

Daftar Isi