The complete Dbrij Storage reference: buckets, objects, presigned uploads, the S3 endpoint, access keys, versioning, lifecycle, public links and plans.
Dbrij Storage keeps files. Two faces, one allowance: Buckets, which your code reaches through the REST API below or through the S3 protocol with any tool that speaks it, and Files, the drive people use from the app with folders, sharing and editors. A person's storage is theirs; a company's is the company's, and every meter and cap is by that principal.
Bytes live on an S3 compatible object store with traffic included, which is why no plan carries an egress fee. Uploads go straight to the store through presigned URLs, so a large file never streams through the API.
https://api.dbrij.com/api/storagehttps://s3.dbrij.comhttps://files.dbrij.comREST calls carry Authorization: Bearer <accessKeyId>:<secret>. S3 calls sign with the same id and secret as AWS credentials. Keys are minted on the Access keys page; the secret is shown once.
Three calls put a file in a bucket and read it back. Every step also works with the AWS CLI, shown afterwards.
curl https://api.dbrij.com/api/storage/buckets \
-H "Authorization: Bearer dbsk_…:dbss_…" \
-H "Content-Type: application/json" \
-d '{ "name": "my-assets" }'curl https://api.dbrij.com/api/storage/buckets/<bucketId>/upload-url \
-H "Authorization: Bearer dbsk_…:dbss_…" \
-H "Content-Type: application/json" \
-d '{ "key": "images/logo.png", "contentType": "image/png" }'
# → { "url": "https://…", "headers": { "Content-Type": "image/png" } }
curl -X PUT "<url from the response>" -H "Content-Type: image/png" --data-binary @logo.pngcurl https://api.dbrij.com/api/storage/buckets/<bucketId>/confirm \
-H "Authorization: Bearer dbsk_…:dbss_…" \
-H "Content-Type: application/json" \
-d '{ "key": "images/logo.png" }'export AWS_ACCESS_KEY_ID=dbsk_…
export AWS_SECRET_ACCESS_KEY=dbss_…
aws --endpoint-url https://s3.dbrij.com s3 mb s3://my-assets
aws --endpoint-url https://s3.dbrij.com s3 cp logo.png s3://my-assets/images/logo.png
aws --endpoint-url https://s3.dbrij.com s3 ls s3://my-assets/images/Over S3 there is no confirm step: the store tells Dbrij when the bytes land. Use whichever surface your stack already speaks. Most frameworks have an S3 storage driver, and pointing it here is a two line change.
An access key is an id (dbsk_…) and a secret (dbss_…) with a scope of read, write or both. A key made in your personal space reaches your buckets. A key made while working in a company reaches the company's buckets and bills to the company. The secret is shown once, at creation.
WHAT EACH SCOPE ALLOWS
| read | scope | List buckets and objects, download, and every S3 GET, HEAD and LIST. |
| write | scope | Create buckets, upload, delete, copy, multipart, and bucket configuration over S3. |
rclone config create dbrij s3 provider=Other access_key_id=dbsk_… secret_access_key=dbss_… endpoint=https://s3.dbrij.com
rclone sync ./public dbrij:my-assets/publicimport { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({
endpoint: 'https://s3.dbrij.com',
region: 'auto',
forcePathStyle: true,
credentials: { accessKeyId: process.env.DBRIJ_KEY_ID!, secretAccessKey: process.env.DBRIJ_KEY_SECRET! },
});
await s3.send(new PutObjectCommand({ Bucket: 'my-assets', Key: 'images/logo.png', Body: bytes, ContentType: 'image/png' }));Path style (https://s3.dbrij.com/my-assets/key) is always accepted. Virtual host style (my-assets.s3.dbrij.com) works too. Presigned S3 URLs from any SDK verify the same way.
Rotation. Mint the new key, move your deployments to it, then revoke the old one. A revoked key fails every call immediately with 401. Keys made before the S3 endpoint existed show as REST only on the keys page and cannot sign S3 requests; mint a new one.
Scoped keys (Pro plan and up) are narrowed at creation: one bucket, a prefix under it, an end date, an IP allowlist. Out of scope calls fail with 403 on REST and AccessDenied over S3; an expired key fails with 401. A key for one service that can only write uploads/ in one bucket and stops working next quarter is the key to put in that service's environment.
@dbrij/storage is a zero dependency client for Node 18+ and browsers. It covers what S3 does not: delivery links, transformations, presets, and the confirm step REST uploads need. Keep using an AWS SDK for everything else if you already do.
npm install @dbrij/storageimport { DbrijStorage, transform } from '@dbrij/storage';
const storage = new DbrijStorage({ keyId: process.env.DBRIJ_KEY_ID, secret: process.env.DBRIJ_KEY_SECRET });
const bucket = await storage.buckets.create('my-assets');
await storage.objects.upload(bucket.id, 'images/logo.png', bytes, { contentType: 'image/png' });
const link = await storage.delivery.url(bucket.id, 'images/logo.png', { transform: transform({ width: 800, format: 'auto' }) });
// link.url → https://files.dbrij.com/t/w_800,f_auto/<bucketId>/images/logo.pngimport { DbrijStorage } from '@dbrij/storage';
const file = await DbrijStorage.presetUpload('dbup_…', input.files[0], { onProgress: (f) => bar.style.width = f * 100 + '%' });
img.src = file.deliveryUrl;METHODS
| buckets.list / create | key | The buckets the key can reach; create a private or public one. |
| objects.list / upload / delete / downloadUrl | key | upload presigns, PUTs the bytes and confirms in one call, with progress in browsers. |
| delivery.url | key | A delivery link, signed for private objects, transformed for images. |
| DbrijStorage.presetUpload | no key | A keyless upload through a preset, for browsers and mobile. |
| transform(...) | helper | Named options → the token string, so code reads width: 800 rather than w_800. |
Python, Go and PHP: point the AWS SDK for that language at https://s3.dbrij.com with the key id and secret, and call the REST endpoints in this reference for delivery links and presets. Dedicated packages follow.
A bucket is a named space for objects. It is private by default: nothing in it can be read without a signed URL or a key. Switching it public makes every object's stable link readable by anyone. A public bucket can also be put on signed, expiring links instead of permanent ones, for downloads you want to be able to withdraw.
/storage/bucketsreadList buckets
Every bucket the key can reach: a personal key sees the person's buckets, a company key the company's. Counts and bytes ride along.
curl -X GET https://api.dbrij.com/api/storage/buckets \
-H "Authorization: Bearer $DBRIJ_API_KEY"{
"success": true,
"data": [{ "id": "b1…", "name": "my-assets", "visibility": "private", "versioning": null, "objectCount": 128, "totalBytes": 91827364, "createdAt": "…" }]
}/storage/bucketswriteCreate a bucket
Names are 3 to 63 lowercase letters, numbers and hyphens, unique per owner. A public bucket serves permanent links from the moment it exists.
PATH / BODY PARAMETERS
| name* | string | The bucket name, used in every S3 path. |
| visibility | "private" | "public" | Private by default: objects are reachable only through signed URLs. |
curl -X POST https://api.dbrij.com/api/storage/buckets \
-H "Authorization: Bearer $DBRIJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "my-assets", "visibility": "private" }'{ "name": "my-assets", "visibility": "private" }{
"success": true,
"data": { "id": "b1…", "name": "my-assets", "visibility": "private", "objectCount": 0, "totalBytes": 0 }
}Over S3, CreateBucket, ListBuckets, DeleteBucket, PutBucketVersioning, PutBucketLifecycleConfiguration and PutBucketPolicy are all served. Deleting a bucket needs it empty, as everywhere.
Keys are paths: slashes make folders, and a key ending in a slash is a folder marker with no bytes. Objects can be up to five gigabytes each, subject to the plan's allowance. Uploads never stream through the API: REST hands you a presigned PUT, S3 uploads go to the endpoint directly, multipart included.
/storage/buckets/:bucketId/objectsreadList objects
Objects in a bucket, optionally under a key prefix. Folder markers (keys ending in a slash) come back with isFolder true and no bytes.
PATH / BODY PARAMETERS
| prefix | string | Query parameter. Only keys starting with this, e.g. images/. |
curl -X GET https://api.dbrij.com/api/storage/buckets/:bucketId/objects \
-H "Authorization: Bearer $DBRIJ_API_KEY"{
"success": true,
"data": [{ "key": "images/logo.png", "size": 18233, "contentType": "image/png", "isFolder": false, "url": null, "updatedAt": "…" }]
}/storage/buckets/:bucketId/upload-urlwriteGet a presigned upload URL
Step one of an upload. The URL points at the object store, not the API: PUT the raw bytes there with the headers returned, with no Authorization header and no JSON envelope. It expires after fifteen minutes.
PATH / BODY PARAMETERS
| key* | string | The object key, up to 1,024 characters. Slashes make folders. |
| contentType | string | Sent as the object's Content-Type. Defaults to application/octet-stream. |
curl -X POST https://api.dbrij.com/api/storage/buckets/:bucketId/upload-url \
-H "Authorization: Bearer $DBRIJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "key": "images/logo.png", "contentType": "image/png" }'{ "key": "images/logo.png", "contentType": "image/png" }{
"success": true,
"data": { "url": "https://…", "method": "PUT", "headers": { "Content-Type": "image/png" }, "key": "images/logo.png", "expiresIn": 900 }
}/storage/buckets/:bucketId/confirmwriteConfirm an upload
Step two. Dbrij reads the object's real size from the store and records it. An upload that is never confirmed is swept away after a day, so the confirm is what makes an object exist.
PATH / BODY PARAMETERS
| key* | string | The key you uploaded to. |
curl -X POST https://api.dbrij.com/api/storage/buckets/:bucketId/confirm \
-H "Authorization: Bearer $DBRIJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "key": "images/logo.png" }'{ "key": "images/logo.png" }{
"success": true,
"data": { "key": "images/logo.png", "size": 18233, "contentType": "image/png", "url": null }
}/storage/buckets/:bucketId/object?key=…readDownload an object
Redirects (302) to a signed URL for the bytes, good for one hour. Follow the redirect, or read the Location header to hand the URL to a browser.
curl -X GET https://api.dbrij.com/api/storage/buckets/:bucketId/object?key=… \
-H "Authorization: Bearer $DBRIJ_API_KEY"/storage/buckets/:bucketId/object?key=…writeDelete an object
On a versioned bucket this writes a delete marker and keeps the previous version; on an unversioned bucket the bytes are gone.
curl -X DELETE https://api.dbrij.com/api/storage/buckets/:bucketId/object?key=… \
-H "Authorization: Bearer $DBRIJ_API_KEY"{
"success": true,
"data": { "deleted": true }
}Big files. Use S3 multipart for anything over a hundred megabytes: CreateMultipartUpload, UploadPart, ListParts, CompleteMultipartUpload and UploadPartCopy are served, and every SDK's high level upload helper uses them for you.
Copy. CopyObject copies within and between your buckets on the server, no download.
Browser uploads. The presigned URL from the REST call is safe to hand to a browser: it carries no key and expires in fifteen minutes. Bucket CORS is configured for direct browser PUTs.
Three kinds of link, depending on what the bucket allows.
LINK TYPES
| Stable public link | public bucket, or a public prefix | Permanent, cacheable, no signature. The object's url field in every listing. Overwrite the key and the link serves the new bytes. |
| Signed read | any bucket | A URL good for one hour from the download endpoint, or for any lifetime you sign with the S3 SDK (GetObject presign). Shareable, expiring, withdrawable by rotating the key. |
| Signed public link | public bucket with signed reads on | For public buckets that should not be hotlinkable forever: links carry a signature and expire, but no key is needed to mint them. |
https://files.dbrij.com/<bucketId>/images/logo.png # a public object
https://files.dbrij.com/<bucketId>/images/logo.png?dl=1 # served as a download
https://files.dbrij.com/<bucketId>/private.pdf?exp=1760000000&sig=… # a private object, signed
https://files.dbrij.com/t/w_800,c_fill,f_auto/<bucketId>/images/logo.png # transformed/storage/buckets/:bucketId/delivery-urlreadMake a delivery link
Public objects get a permanent link. Private objects, and public buckets set to signed reads, get a signed link that expires. Add a transformation for images.
PATH / BODY PARAMETERS
| key* | string | The object key. |
| transform | string | Transformation tokens, e.g. w_800,c_fill,f_auto. Images only. |
| expiresIn | number | Seconds a signed link lives. Default 3600, max thirty days. |
| download | boolean | Serve with Content-Disposition: attachment. |
curl -X POST https://api.dbrij.com/api/storage/buckets/:bucketId/delivery-url \
-H "Authorization: Bearer $DBRIJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "key": "images/hero.jpg", "transform": "w_1200,f_auto,q_80", "expiresIn": 86400 }'{ "key": "images/hero.jpg", "transform": "w_1200,f_auto,q_80", "expiresIn": 86400 }{
"success": true,
"data": { "url": "https://…/t/w_1200,f_auto,q_80/<bucketId>/images/hero.jpg?exp=…&sig=…", "expiresAt": "…", "signed": true }
}The delivery host streams the bytes with range support, ETag and Last-Modified, so browsers revalidate cheaply and video seeks. Every byte served counts against the plan's transfer allowance; there is no egress fee. The free plan stops serving at its allowance for the period, paid plans keep serving and bill the extra per GB.
Put tokens in the path and the delivery host resizes, crops and re-encodes the image on first request, caches the result, and serves it from the cache after that. Only images transform; anything else is served as is. A transformation counts against the plan's allowance the first time it runs, never on a cache hit.
https://files.dbrij.com/t/<tokens>/<bucketId>/<key>
https://cdn.example.com/t/<tokens>/<key> # on a custom domainTOKENS
| w_800, h_600 | size | Width and height in pixels, up to 4096. One alone keeps the aspect ratio. |
| c_fill | fit | scale | pad | crop | crop | fill covers the box and crops; fit sits inside it without enlarging; scale stretches; pad letterboxes with the background; crop cuts to the box. |
| g_auto | center | north | south | east | west | northeast … | gravity | Where fill and crop keep. auto picks the busiest region. |
| f_auto | webp | avif | jpg | png | gif | format | auto serves AVIF or WebP when the browser accepts it, else the source format, and never drops transparency into JPEG. |
| q_80 | quality | 1 to 100. Defaults suit each format. |
| dpr_2 | density | Multiplies w and h for high density screens, up to 3. |
| r_20 | r_max | corners | Rounded corners in pixels, or max for a circle. |
| b_ffffff | background | Hex colour behind pad and under transparency flattened into JPEG. |
| a_90 | rotate | Quarter turns clockwise. EXIF orientation is honoured on its own. |
| e_blur:200, e_grayscale, e_sharpen | effects | Blur strength up to 2000, greyscale, and a sharpen pass. |
| flip, flop | mirror | Vertical and horizontal mirror. |
Unknown tokens are refused with the token named, never ignored. Sources up to 40 MB transform; SVG and icons are served untouched. Cloudinary style paths (/image/upload/w_300,c_fill/v1712/folder/file.jpg) are read too, on the delivery host after the bucket id and on custom domains directly, so URLs copied from an existing site keep working after a switch.
An upload preset opens one bucket prefix to keyless uploads from a browser or a mobile app, inside limits you set: accepted types, a size cap, and the origins allowed. The preset token (dbup_…) is safe in page source. Create presets on the bucket page.
<script src="https://app.dbrij.com/storage-widget.js"></script>
<button id="upload">Upload files</button>
<script>
DbrijStorage.attach(document.querySelector('#upload'), {
preset: 'dbup_…',
onUploaded: (file) => console.log(file.key, file.deliveryUrl),
});
// or without UI: DbrijStorage.upload('dbup_…', fileInput.files[0]).then(console.log)
</script>/storage/uploads/:presetTokennoneStart a preset upload
No key. Returns a presigned PUT for a key under the preset prefix; PUT the bytes there with the headers given, then confirm.
PATH / BODY PARAMETERS
| filename* | string | Used for the key's last segment, sanitised. |
| contentType | string | Checked against the preset's allowed types. |
| size | number | Checked against the cap up front; the real size is checked again at confirm. |
curl -X POST https://api.dbrij.com/api/storage/uploads/:presetToken \
-H "Authorization: Bearer $DBRIJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "filename": "avatar.png", "contentType": "image/png", "size": 18233 }'{ "filename": "avatar.png", "contentType": "image/png", "size": 18233 }{
"success": true,
"data": { "uploadUrl": "https://…", "method": "PUT", "headers": { "Content-Type": "image/png" }, "key": "uploads/2026/09/1a2b3c4d-avatar.png", "expiresIn": 900, "confirmUrl": "…" }
}/storage/uploads/:presetToken/confirmnoneConfirm a preset upload
Reads what actually landed. Past the size cap or outside the allowed types, the bytes are deleted and the call fails; otherwise the object exists and a delivery link good for a day comes back.
curl -X POST https://api.dbrij.com/api/storage/uploads/:presetToken/confirm \
-H "Authorization: Bearer $DBRIJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "key": "uploads/2026/09/1a2b3c4d-avatar.png" }'{ "key": "uploads/2026/09/1a2b3c4d-avatar.png" }{
"success": true,
"data": { "key": "…", "size": 18233, "contentType": "image/png", "url": null, "deliveryUrl": "https://…?exp=…&sig=…" }
}Put your own hostname in front of a bucket. cdn.example.com/images/hero.jpg serves the object, cdn.example.com/t/w_800/images/hero.jpg the transformation, and Cloudinary style paths are read as well. The plan sets how many domains a space may attach.
DNS
| CNAME | recommended | Point the hostname at files.dbrij.com. Verification passes on the CNAME alone and a certificate is issued on first request. |
| TXT | ownership only | Put the token from the bucket page at _dbrij.<hostname>. Use it when the hostname must stay pointed elsewhere while you prove ownership. |
Add the domain on the bucket page, add the record, press Check. Signed links work on custom domains too; the signature does not depend on the host.
Turn versioning on for a bucket and every overwrite keeps the previous bytes as a version, every delete writes a delete marker instead of removing anything, and deleting the marker brings the object back. Versions count against the allowance until a lifecycle rule or an explicit delete removes them.
aws --endpoint-url https://s3.dbrij.com s3api put-bucket-versioning \
--bucket my-assets --versioning-configuration Status=Enabledaws --endpoint-url https://s3.dbrij.com s3api list-object-versions --bucket my-assets --prefix images/logo.png
aws --endpoint-url https://s3.dbrij.com s3api delete-object --bucket my-assets --key images/logo.png --version-id <marker id>Lifecycle rules run every hour. The applied subset is Expiration.Days (current objects older than this are deleted, a delete marker on a versioned bucket), NoncurrentVersionExpiration.NoncurrentDays (previous versions removed for good) and ExpiredObjectDeleteMarker. Transitions, tag filters and date based rules are refused with a reason rather than silently ignored.
aws --endpoint-url https://s3.dbrij.com s3api put-bucket-lifecycle-configuration --bucket my-assets --lifecycle-configuration '{
"Rules": [{ "ID": "prune-versions", "Status": "Enabled", "Filter": { "Prefix": "" },
"NoncurrentVersionExpiration": { "NoncurrentDays": 30 } }]
}'A whole bucket can be public, or just a prefix of it: public/ readable by anyone while everything else stays private. Set it from the bucket page, or with a bucket policy over S3. Dbrij reads the policy and maps it to what it enforces; anything finer than "public read on this prefix" is refused with a reason, never accepted and silently unenforced.
aws --endpoint-url https://s3.dbrij.com s3api put-bucket-policy --bucket my-assets --policy '{
"Version": "2012-10-17",
"Statement": [{ "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::my-assets/public/*" }]
}'Object level ACLs (x-amz-acl: public-read) on a private bucket are refused with guidance, because an ACL that quietly did nothing is the kind of thing that leaks a file a year later.
Object events delivered to your own https endpoints (Starter plan and up), signed the way every Dbrij webhook is signed. Add endpoints on the Webhooks page; each one gets a secret shown once and a delivery log you can replay from.
EVENTS
| object.created | event | An object was uploaded, overwritten, copied or imported. data: bucketId, key, size, contentType, versionId. |
| object.deleted | event | An object was deleted through the console, the REST API or S3. data: bucketId, key. |
| import.finished | event | An import completed. data: bucketId, importId, source, copied, skipped, failed, bytes. |
| bucket.created / bucket.deleted | event | data: bucketId, name. |
POST https://example.com/hooks/dbrij
Content-Type: application/json
X-Dbrij-Event: object.created
X-Dbrij-Signature: sha256=<hmac(secret, body)>
X-Dbrij-Signature-V2: t=1760000000,sha256=<hmac(secret, "1760000000." + body)>
{ "id": "evt_…", "type": "object.created", "createdAt": "…", "data": { "bucketId": "…", "key": "images/logo.png", "size": 18233, "contentType": "image/png" } }import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(secret, body, header) {
const [t, sig] = header.split(',').map((p) => p.split('=')[1]);
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac('sha256', secret).update(t + '.' + body).digest('hex');
return timingSafeEqual(Buffer.from(expected), Buffer.from(sig));
}Reply with any 2xx within ten seconds. Anything else is retried four more times over a few hours; twenty consecutive failures switch the endpoint off until you turn it back on. Deliveries are kept for thirty days.
Files is the drive: what a person or a company keeps without writing code. Folders, sharing with members or by email, links anyone can open, trash, versions of edited files, and editors for documents, spreadsheets and presentations. It sits on the same allowance as the buckets, so a company buys storage once.
WHAT LIVES WHERE
| Files | people | Drive and Documents in the app and on mobile. Sharing, links, editors, trash. Counted as driveBytes. |
| Buckets | code | Objects reached by key or by S3. Public links, presigned uploads, versioning, lifecycle. Counted as bucketBytes. |
| Database backups | hosting | Nightly dumps from hosted databases, kept in the same store and counted against the same allowance. |
Import from Google Drive, Dropbox and OneDrive, and mounting Files as a disk over WebDAV, are on the roadmap and land with the switching tools below.
Because the endpoint speaks S3, most moves are a change of endpoint and key. This is what changes in the common stacks, and what does not.
FROM AN S3 COMPATIBLE STORE (AWS S3, CLOUDFLARE R2, BACKBLAZE B2, WASABI, DIGITALOCEAN SPACES, MINIO, SUPABASE STORAGE)
| Endpoint | change | https://s3.dbrij.com, path style on. Region can stay whatever it was. |
| Credentials | change | A Dbrij access key id and secret in place of the old pair. |
| Bucket names | keep | Create buckets with the same names and your keys and paths stay identical. |
| Your code | keep | SDK calls, presigned URLs and multipart uploads are unchanged. |
| Copying the data | built in | Open the bucket, Delivery → Import, paste the source endpoint, bucket and keys, and Dbrij copies everything in the background (dry run first if you like). Sync mode skips what is already here, so run it again right before cutover. rclone works too. |
| Moving the hostname first | lazy fetch | Set the old bucket URL as the origin on the bucket page and point your delivery domain here now: any key not yet copied is pulled across on first request and kept. |
rclone config create old s3 provider=AWS access_key_id=… secret_access_key=… region=eu-west-1
rclone config create dbrij s3 provider=Other access_key_id=dbsk_… secret_access_key=dbss_… endpoint=https://s3.dbrij.com
rclone sync old:my-assets dbrij:my-assets --transfers 16 --checksum
# run it again just before cutover: only what changed is copiedFROM CLOUDINARY
| Uploads | change | An unsigned upload preset replaces the upload preset; the widget replaces the upload widget; signed uploads become a presigned PUT or an S3 PutObject. |
| Delivery URLs | mostly keep | The delivery host reads Cloudinary path syntax (/image/upload/w_300,c_fill/v123/folder/file.jpg) on a custom domain, so a CNAME from your media hostname keeps existing URLs serving. The common transformation tokens are the same. |
| Copying the data | built in | Open the bucket, Delivery → Import, choose Cloudinary and paste the cloud name, API key and secret. Every image, video and raw file is copied with its public id as the key, so /image/upload paths keep resolving. |
| Lazy fetch | zero downtime | Set https://res.cloudinary.com/<cloud>/image/upload as the origin and CNAME your media hostname here: anything not yet imported is pulled on first request. |
FROM FIREBASE STORAGE
| Uploads | change | Firebase's SDK uploads become presigned PUTs or S3 PutObject calls. |
| Copying the data | built in | Google Cloud Storage offers S3 interoperability keys: make a pair in the Cloud console, then run an S3 import with endpoint https://storage.googleapis.com and your bucket name. |
FROM GOOGLE DRIVE, DROPBOX OR ONEDRIVE (FOR PEOPLE)
| Google Drive | built in | In Files, New → Import from Google Drive. Sign in once and the whole drive is copied in the background with its folders intact; Google Docs, Sheets and Slides arrive as Word, Excel and PowerPoint files you can keep editing. |
| Dropbox, OneDrive | today | Download folders and drop them into Files; the drop zone takes whole folders. A one click import for these is next. |
The allowance is one number for the drive and the buckets together. A paid Hosting plan's included storage still counts: the allowance is the sum. Free stops uploads at the allowance. Paid plans keep accepting uploads and bill the gigabytes held above it by the day, at renewal, from the closed period, never the live month. There is no egress fee on any plan.
| Plan | Storage | Transfer a month | Buckets | Keys | Over the allowance | Versioning |
|---|---|---|---|---|---|---|
| Free | 5 GB | 25 GB | 2 | 2 | Uploads stop | No |
| Starter | 100 GB | 500 GB | 25 | 10 | ₦25 per GB month | Yes |
| Pro | 500 GB | 2 TB | 1,000 | 50 | ₦25 per GB month | Yes |
| Business | 2.5 TB | 10 TB | 1,000 | 200 | ₦25 per GB month | Yes |
PLATFORM LIMITS
| Object size | 5 GB | Per object, on every plan. Use multipart above a hundred megabytes. |
| Key length | 1024 characters | UTF-8, no NUL. Slashes make folders. |
| Presigned upload | 15 minutes | Then mint another. Unconfirmed uploads are swept after a day. |
| Signed download | 60 minutes | From the REST download endpoint. Sign your own lifetime over S3. |
ERRORS WORTH HANDLING
| 400 | bad request | A bad bucket name, a key past the limit, a plan cap reached (buckets, keys, or storage on Free). The message says which, and where to change it. |
| 401 | unauthorized | Missing, revoked or wrong key. Over S3: InvalidAccessKeyId or SignatureDoesNotMatch. |
| 403 | forbidden | The key is real but lacks the scope, or reaches for a bucket it does not own. |
| 404 | not found | No bucket by that id, or no object by that key. |
| 501 | not implemented | Over S3, a bucket policy or lifecycle rule finer than the enforced subset. The reason names the clause. |
| 503 | unavailable | The object store is not reachable. Existing links keep working through the signed path; retry shortly. |
Dbrij is Nigeria’s company operating system: the first Nigerian built platform to put team chat, video meetings, email, HR, tax compliant payroll, customer support, marketing, analytics and cloud hosting in one product on one subscription.