Storage
See how much your services store, when files are deleted and download the ones you need. The Storage API is read-only: this guide shows you how to use it.
7 min readReviewed on
Goal
Your Hablame services store files for you, for example call recordings, audio or documents. With this API you can see how much they take up, what will be deleted soon, how long each file type is kept, what price applies to you and, if your key allows it, list the files and download them.
Read-only
With the API you cannot store, delete or change the retention time of a file: that is done from the customer portal (Storage). Any method other than GET answers 405. Your files are stored continuously and never pause, even without balance.
Requirements
- An active organization and a valid API key. How to get one.
- The Storage service (
storage) on the key. With it, the key sees everything: summary, usage, retention, price, files and downloads. Without it, the seven endpoints answer403 AUTH_SERVICE_NOT_ALLOWED. Only the owner of your organization creates keys. - To see the price, your organization needs to have its currency defined; otherwise the price fields come as
null.
Credentials
Every request carries Authorization:. The Storage service is granted in full, and files may contain sensitive data (a recording, a document): a key with the service can see and download them.
| Key | What it can query |
|---|---|
| Key with the Storage service | Everything: summary, usage, retention, price, the file list, the detail of each one and the download links. |
| Key without the service | Nothing in Storage: every endpoint answers 403 AUTH_SERVICE_NOT_ALLOWED. |
Steps
- 01See the summary
GET /returns what your files take up now (in total, per service and per type), today’s price, the month’s usage and how many files are deleted in the next 24 hours and 7 days.storage/ summary - 02Review the usage
GET /brings the GB-month series per day, per month or per hour, and compares it with the previous range of the same length.storage/ usage - 03Query retention
GET /says how long each file type is kept before it is deleted, with the minimum, the maximum and the default.storage/ retention - 04Query the price
GET /returns the price per GB-month in force today and the next one if already set. This documentation does not publish figures.storage/ pricing - 05List your files
GET /returns them newest first, paginated by cursor. Filter bystorage/ objects service,scopeand dates. - 06Download one
GET /returns a signed, temporary link. Download the file with that link.storage/ objects/ {id}/ download-url
Examples
Query the summary
curl https://developers.hablame.co/api/v6/storage/summary \
-H 'Authorization: Bearer hk_TU_API_KEY'const res = await fetch('https://developers.hablame.co/api/v6/storage/summary', {
headers: { Authorization: `Bearer ${process.env.HABLAME_API_KEY}` },
})
const { success, data, error } = await res.json()
if (success) console.log(data.now.bytes, 'bytes en', data.now.objects, 'archivos')
else console.error(error.code)import os
import requests
r = requests.get(
"https://developers.hablame.co/api/v6/storage/summary",
headers={"Authorization": f"Bearer {os.environ['HABLAME_API_KEY']}"},
timeout=30,
)
body = r.json()
if body["success"]:
print(body["data"]["now"]["bytes"], "bytes")Walk through all your files
If next is not null, pass it as cursor in the next request with the same filters. A cursor that was altered, belongs to another organization, uses other filters or is more than 24 hours old answers 400 INVALID_CURSOR: go back to the first page.
const base = 'https://developers.hablame.co/api/v6/storage/objects'
const headers = { Authorization: `Bearer ${process.env.HABLAME_API_KEY}` }
let cursor = null
do {
const url = new URL(base)
url.searchParams.set('limit', '100')
if (cursor) url.searchParams.set('cursor', cursor)
const body = await (await fetch(url, { headers })).json()
if (!body.success) throw new Error(`${body.error.code} (${body.meta.requestId})`)
for (const archivo of body.data.objects) console.log(archivo.id, archivo.name, archivo.size)
cursor = body.data.next
} while (cursor)import os
import requests
base = "https://developers.hablame.co/api/v6/storage/objects"
headers = {"Authorization": f"Bearer {os.environ['HABLAME_API_KEY']}"}
cursor = None
while True:
params = {"limit": 100}
if cursor:
params["cursor"] = cursor
body = requests.get(base, headers=headers, params=params, timeout=30).json()
if not body["success"]:
raise RuntimeError(f"{body['error']['code']} ({body['meta']['requestId']})")
for archivo in body["data"]["objects"]:
print(archivo["id"], archivo["name"], archivo["size"])
cursor = body["data"]["next"]
if not cursor:
breakDownload a file
# 1. Request the temporary link (valid for 15 minutes unless you set another period)
curl 'https://developers.hablame.co/api/v6/storage/objects/obj_01JABCDEFGHJKMNPQRSTVWXYZ0/download-url?minutes=15' \
-H 'Authorization: Bearer hk_TU_API_KEY'
# 2. Download with the URL in data.url (it does not carry your key)
curl -o archivo.wav 'URL_DE_DATA.URL'Expected responses
The list returns files with this shape. The values are examples:
{
"success": true,
"data": {
"objects": [
{
"id": "obj_01JABCDEFGHJKMNPQRSTVWXYZ0",
"name": "llamada-3001234567.wav",
"service": "callblasting",
"scope": "recordings",
"size": 482113,
"contentType": "audio/wav",
"storedAt": "2026-10-18T10:00:00.000-05:00",
"expiresAt": "2026-11-17T10:00:00.000-05:00",
"status": "available",
"deletable": false
}
],
"next": "c1.t3b9X0k..."
},
"meta": {
"requestId": "5d0f3a9c7b2e41a8",
"clientIp": "203.0.113.10",
"timestamp": "2026-10-19T15:30:00+00:00",
"responseTimeMs": 12.4
}
}statusavailable: it can be downloaded.deleting: it is being deleted or has already expired; it cannot be downloaded and is billed until it finishes.expiresAt- When it is deleted under the current retention time, or
nullif it does not expire. size- Size in bytes. The billing GB are decimal (10⁹ bytes).
Importes- All amounts are in micros of your organization’s currency: units multiplied by 10⁶. The summary and the price carry them in
currencyand... . Micro
The download link answers { url,. It is valid until expiresAt: 15 minutes by default, 60 at most (minutes parameter), and never beyond when the file is deleted.
Common errors
| Code | What happened | What to do |
|---|---|---|
403 AUTH_SERVICE_NOT_ALLOWED | Your key does not have the Storage service. | Ask the owner of your organization for a key with Storage. |
404 STORAGE_OBJECT_NOT_FOUND | The file does not exist for your organization. The answer is the same for one that never existed, one from another organization, a deleted one, one being deleted (when asking for its download) or a malformed id. | Take the id exactly as it comes in the list. |
400 INVALID_FILTER | A parameter is not valid: a service that does not exist, a scope without service, a date or a range outside what is allowed. | Check the parameters in the reference. |
400 INVALID_CURSOR | The cursor was altered, belongs to another organization, other filters or is more than 24 hours old. | Go back to the first page. |
405 METHOD_NOT_ALLOWED | You used a method other than GET. | The Storage API is read-only. |
429 RATE_TPS_EXCEEDED | The six queries share one quota per organization; download links have another, lower one. | Wait for Retry-After. Do not ask for a link for every row: ask when you are about to download. |
503 SERVICE_UNAVAILABLE | Storage is not responding right now. | Retry after Retry-After. |
Security
- Give the Storage service only to whoever needs it. With it, the key sees and downloads the files, which may be call recordings or documents of your organization.
- The download link is a short-lived secret. Do not store or share it; ask for a new one every time you need it.
- Each link is recorded with the API key that asked for it and the IP you called from, in your organization’s audit log.
- The API never shows a file’s internal location or sequential identifiers: a file that belongs to someone else answers the same as one that does not exist.
- Restrict the key’s allowed networks to your server’s IPs.
Related endpoints
| Endpoint | What it does |
|---|---|
GET / | Summary |
GET / | Usage |
GET / | Retention |
GET / | Storage pricing |
GET / | List files |
GET / | Get a file |
GET / | Download link |
The six queries share one quota per organization (the download link has its own). All seven require the Storage service (storage) on the key.
Next steps
Every endpoint with its parameters, responses and errors.
How to pace your client with the RateLimit-* headers.
How to ask for a key with Storage.
Every code and how to fix it.
For technical questions, email developers@hablame.co. If you have a production incident, message us on WhatsApp at +57 319 660 0002, 24/7.