Abrova
☰ Docs menu · REST API
Coming soon. Abrova Storage is coming soon. This page describes how it will work; it can’t be used yet.

REST API

Direct HTTP API for Abrova Storage — authentication headers (API key, server secret, Abrova user token), error codes, bucket endpoints, file upload/download/delete endpoints, public links and public image URLs, signed links, and the image transform endpoint with query parameters.

Last updated: 27 Oct 2026

Use the REST API directly without an SDK. All requests go to https://storage.abrova.ir.

Authentication Headers#

bash
# Required for all /api/v1 requests
x-api-key: ab_live_xxxxxxxxxxxx

# From apps: the signed Abrova user token of the signed-in user.
# Required on every file and transform request from an app
# (minted by your server or Abrova Auth, see "Sign users in" on the Overview page)
x-user-token: eyJhbGciOi...

# From your server only. Skips access rules; no user token needed.
# Required for bucket and access-rule changes and signed links
x-server-secret: ab_srv_xxxxxxxxxxxx

# Content type for JSON requests
Content-Type: application/json

Listing and reading buckets and getting a file's public link need only x-api-key. Public links (/public/…) need no headers at all.

Error codes

Every error answers { "statusCode": 401, "code": "token_expired", "message": "…", "timestamp": "…" }. Switch on code; show message to your users. The SDKs expose the codes as StorageErrorCode constants.

StatuscodeMeaning
400invalid_pathThe path was refused (a .. segment, control characters, a name starting or ending with a space, too long; new paths also can't contain ?, # or %).
400type_not_allowedThe bucket doesn't allow this file's type.
400file_too_largeThe file is larger than the bucket's size limit.
400executable_blockedThe bucket blocks executable files and this one is (by name or content).
400file_requiredThe upload had no file part.
400invalid_idsdelete-many needs a list of 1 to 1,000 file ids.
400invalid_rulesAn access rule is wrong (the message names the first problem).
400upload_rejectedconfirm-upload: the signed upload was refused (size, type, rules or usage).
401token_requiredAn app file or transform request had no x-user-token.
401token_expiredThe user token has expired. Get a new one and retry.
401token_invalidThe user token is not valid (bad signature, another project, not an Abrova user token).
403access_deniedThe bucket's access rules refused the request.
403server_secret_requiredBucket, rule and signed-link actions need x-server-secret; call them from your server.
404—Not found (also a /public link of a private or unknown file).
409path_takenA file already exists at this path.
409upload_missing, upload_expiredconfirm-upload: nothing was uploaded yet, or the link expired.
429—Too many requests. Wait and retry.

Image transforms have their own codes, listed under Transform errors below.

Bucket Endpoints#

List buckets

bash
# GET /api/v1/buckets
curl -X GET "https://storage.abrova.ir/api/v1/buckets" \
  -H "x-api-key: ab_live_xxx"

# Response
[
  {
    "id": "8f0c6d1e-…",
    "name": "my-bucket",
    "description": null,
    "visibility": "private",
    "allowedMimeTypes": ["image/*"],
    "maxFileSize": 10485760,
    "blockExecutables": true,
    "createdAt": "2026-10-23T09:15:02.123Z",
    "updatedAt": "2026-10-23T09:15:02.123Z"
  }
]

allowedMimeTypes is null when every type is allowed. GET /api/v1/buckets/:id, GET /api/v1/buckets/name/:name and GET /api/v1/buckets/:id/stats ({ "fileCount": 12, "totalSize": 4096000 }) work the same way.

Create bucket

Needs x-server-secret (otherwise 403 server_secret_required). The project comes from the API key.

bash
# POST /api/v1/buckets
curl -X POST "https://storage.abrova.ir/api/v1/buckets" \
  -H "x-api-key: ab_live_xxx" \
  -H "x-server-secret: ab_srv_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "user-uploads",
    "visibility": "private",
    "allowedMimeTypes": ["image/*", "application/pdf"],
    "maxFileSize": 10485760
  }'

PUT /api/v1/buckets/:id (update) and DELETE /api/v1/buckets/:id follow the same rule.

File Endpoints#

From an app, file upload, read, list and delete need x-api-key and x-user-token, and follow the bucket's access rules (a bucket with no rules allows everything to any signed-in user). From your server, send x-server-secret instead of a user token.

Upload (multipart/form-data)

bash
# POST /api/v1/buckets/:bucketId/files
curl -X POST "https://storage.abrova.ir/api/v1/buckets/BUCKET_ID/files" \
  -H "x-api-key: ab_live_xxx" \
  -H "x-user-token: $USER_TOKEN" \
  -F "file=@/path/to/image.jpg" \
  -F "path=images/photo.jpg" \
  -F 'metadata={"category":"profile"}'

# Response
{
  "id": "3b9e2a7c-…",
  "bucketId": "8f0c6d1e-…",
  "path": "images/photo.jpg",
  "fileName": "photo.jpg",
  "mimeType": "image/jpeg",
  "size": 2048576,
  "checksum": "9e107d9d372bb6826bd81d3542a419d6",
  "metadata": { "category": "profile" },
  "uploadedBy": "u_4821",
  "uploadedAt": "2026-10-23T09:15:02.123Z",
  "createdAt": "2026-10-23T09:15:02.123Z",
  "updatedAt": "2026-10-23T09:15:02.123Z"
}

mimeType is read from the file's content. uploadedBy is the user in the token (null for server uploads without one). Fields that are empty are null.

List and get

bash
# GET /api/v1/buckets/:bucketId/files?prefix=images/&limit=100&cursor=…
# → { "files": [ … ], "nextCursor": "…" }   (nextCursor is null on the last page; limit 1-1000, default 100)

# GET /api/v1/files/:fileId                       → the file
# GET /api/v1/buckets/:bucketId/files/<path>      → the file at that path

Download

bash
# GET /api/v1/files/:fileId/download
curl -X GET "https://storage.abrova.ir/api/v1/files/FILE_ID/download" \
  -H "x-api-key: ab_live_xxx" \
  -H "x-user-token: $USER_TOKEN" \
  -o downloaded_file.jpg

This address needs the headers, so it can't be used as a plain <img src> or link.

Public link (public buckets)

bash
# GET /api/v1/files/:fileId/url
curl "https://storage.abrova.ir/api/v1/files/FILE_ID/url" \
  -H "x-api-key: ab_live_xxx"

# Response (file in a public bucket; a private bucket answers 403)
{ "url": "https://storage.abrova.ir/public/<bucketId>/images/photo.jpg" }

The returned url needs no key, token or header: use it in <img src>, Image.network or a share link. Each path segment is percent-encoded. Add image options to the same URL for a resized or converted image (see Public image URLs below). Once a bucket is private, its /public links answer 404.

Signed download link

Needs x-server-secret.

bash
# POST /api/v1/files/:fileId/signed-url   (expiresIn in seconds, max 86400, default 3600)
curl -X POST "https://storage.abrova.ir/api/v1/files/FILE_ID/signed-url" \
  -H "x-api-key: ab_live_xxx" \
  -H "x-server-secret: ab_srv_xxx" \
  -H "Content-Type: application/json" \
  -d '{"expiresIn": 600}'

# Response
{ "url": "https://…" }

Signed upload link

Needs x-server-secret. The bucket's allowed types apply to contentType.

bash
# POST /api/v1/buckets/:bucketId/files/signed-upload-url   (expiresIn default 300, at most 900)
curl -X POST "https://storage.abrova.ir/api/v1/buckets/BUCKET_ID/files/signed-upload-url" \
  -H "x-api-key: ab_live_xxx" \
  -H "x-server-secret: ab_srv_xxx" \
  -H "Content-Type: application/json" \
  -d '{"path": "uploads/photo.jpg", "contentType": "image/jpeg", "expiresIn": 300}'

# Response
{ "url": "https://…", "uploadId": "…", "path": "uploads/photo.jpg", "expiresAt": "…", "maxSize": 10485760 }

# The client then uploads with the same Content-Type:
# curl -X PUT "$URL" -H "Content-Type: image/jpeg" --data-binary @photo.jpg

The file is registered when it is first read or listed; the bucket's size and type limits and your access rules are checked again against the real file, and a file that fails is deleted. To register it at once and get the file back, call POST /api/v1/buckets/:bucketId/files/confirm-upload with { "path": "uploads/photo.jpg" } → { "id", "path", "size", "mimeType" }.

Delete

bash
# DELETE /api/v1/files/:fileId
curl -X DELETE "https://storage.abrova.ir/api/v1/files/FILE_ID" \
  -H "x-api-key: ab_live_xxx" \
  -H "x-user-token: $USER_TOKEN"

# Response
{ "success": true, "message": "File deleted" }

# DELETE /api/v1/buckets/:bucketId/files/<path>   → the same answer
# POST /api/v1/files/delete-many  body: { "ids": ["…", "…"] }  → { "deleted": 2 }

All follow the bucket's delete rules; with x-server-secret instead of a user token, rules are skipped.

Transform Endpoint#

bash
# GET /api/v1/transform/:fileId
# Get a 200x200 thumbnail in WebP format
curl "https://storage.abrova.ir/api/v1/transform/FILE_ID?w=200&h=200&fit=cover&f=webp&q=80" \
  -H "x-api-key: ab_live_xxx" \
  -H "x-user-token: $USER_TOKEN" \
  -o thumb.webp

# Query Parameters
# w       - Width in pixels, 1-4096 (larger values are cut to 4096)
# h       - Height in pixels, 1-4096
# fit     - cover (default), contain, fill, inside, outside
# f       - jpeg (default), png, webp, avif
# q       - Quality 1-100 (default 80)
# blur    - Blur amount 0-20
# sharpen - Sharpen amount 0-10
# rotate  - Degrees (multiples of 90 recommended)

# GET /api/v1/transform/:fileId/metadata
# → { "width": 4032, "height": 3024, "format": "jpeg", "size": 2048576 }

Transforms follow the bucket's read rules and need the headers, so load them with a client that can send headers. Inputs: JPEG, PNG, WebP, GIF, AVIF, HEIC / HEIF, TIFF and SVG. For public files, use a public image URL (below) instead.

Public image URLs

text
https://storage.abrova.ir/public/<bucketId>/<path>?w=600&f=webp

The file's public link plus any of the options above. No key or token: public buckets only. The same options always give the same image, in any order (?w=300&f=webp = ?f=webp&w=300&q=80); other query parameters are ignored. One image keeps at most 50 different option sets. See Image Transforms.

Transform errors

StatuscodeMeaning
400invalid_transformAn option isn't allowed (the message names it).
400not_an_imageThe file isn't an image.
415image_format_not_supportedThe image format can't be resized (for example BMP, PSD, JPEG XL, RAW).
413image_too_largeThe image has more than 50 megapixels.
413source_too_largeThe image file is over 25 MB.
422image_damagedThe image is damaged or incomplete.
403plan_feature_lockedNo transforms left: the free monthly transforms are used up and the wallet cannot pay for more.
429too_many_variantsPublic image URLs: this image already has 50 sizes / formats. Use one you already use.
503transform_busyImage processing is busy. Retry after the Retry-After header's seconds.
Was this page helpful?
Report a problem