API reference
SeleStorage API
All APIs · SDKs · Continue with SeleID (OIDC) · SeleStorage · SeleBase · Webhooks, queue, SeleMail, routing and push · SeleAI · SeleDomains search
Live Version 1.0.0. Tenant-isolated file storage with scoped keys and signed URLs. Keys: ssk_live_ and ssk_test_ (test keys are sandboxed). The service answers 503 selestorage_disabled while switched off.
Base URL
https://storage.seleid.dev Production base URL.
Authentication and scopes
Bearer API key (ssk_live_ or ssk_test_). Scopes: read, write, admin. Test keys are sandboxed from live data.
Rate limits and quotas
Monthly operation and storage quotas by plan are hard stops: a call over the limit returns 429 quota_exceeded and is never billed as overage.
Endpoints
GET /v1/objectsList objectsGET /v1/objects/{key}Download an objectPUT /v1/objects/{key}Upload an objectDELETE /v1/objects/{key}Delete an objectPOST /v1/signed-urlsMint a signed URLGET /v1/signed/{key}Download through a signed URLPUT /v1/signed/{key}Upload through a signed URLGET /v1/usageUsage and limits for the current monthPOST /v1/keysCreate an API keyDELETE /v1/keys/{id}Revoke a keyPOST /v1/keys/{id}/rotateRotate a key
GET /v1/objects
List objects. Scope: read.
Authentication: API key, scope or role read.
| Parameter | In | Required | Description |
|---|---|---|---|
prefix | query | no | Key prefix. |
limit | query | no | Page size. |
cursor | query | no | Cursor from a previous page. |
curl
curl -X GET "https://storage.seleid.dev/v1/objects" \
-H "Authorization: Bearer $SELESTORAGE_KEY"TypeScript
const res = await fetch('https://storage.seleid.dev/v1/objects', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}` },
});
const data = await res.json();Python
import os, requests
res = requests.get(
"https://storage.seleid.dev/v1/objects",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"}
)
print(res.status_code, res.text)
Example response
{
"objects": [
{
"key": "photos/a.jpg",
"size": 1024,
"uploaded": "2026-11-01T10:00:00.000Z"
}
],
"truncated": false,
"cursor": null
}
| Status | Meaning |
|---|---|
200 | A page. |
400 | Invalid request. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
429 | Plan quota or rate limit reached. Hard stop, never an overage charge. |
GET /v1/objects/{key}
Download an object. Scope: read. Served as attachment with nosniff and CSP sandbox.
Authentication: API key, scope or role read.
curl
curl -X GET "https://storage.seleid.dev/v1/objects/YOUR_KEY" \
-H "Authorization: Bearer $SELESTORAGE_KEY"TypeScript
const res = await fetch('https://storage.seleid.dev/v1/objects/YOUR_KEY', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}` },
});
const data = await res.json();Python
import os, requests
res = requests.get(
"https://storage.seleid.dev/v1/objects/YOUR_KEY",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"}
)
print(res.status_code, res.text)
| Status | Meaning |
|---|---|
200 | Object bytes. |
400 | Invalid request. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
404 | Not found. |
429 | Plan quota or rate limit reached. Hard stop, never an overage charge. |
PUT /v1/objects/{key}
Upload an object. Scope: write. Content-Length is required. Max size per object: Free 25 MB, Starter 100 MB, Pro 500 MB.
Authentication: API key, scope or role write.
curl
curl -X PUT "https://storage.seleid.dev/v1/objects/YOUR_KEY" \
-H "Authorization: Bearer $SELESTORAGE_KEY" \
--data-binary @fileTypeScript
const res = await fetch('https://storage.seleid.dev/v1/objects/YOUR_KEY', {
method: 'PUT',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}` },
body: await readFile('./file'),
});
const data = await res.json();Python
import os, requests
res = requests.put(
"https://storage.seleid.dev/v1/objects/YOUR_KEY",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"},
data=open("file", "rb")
)
print(res.status_code, res.text)
Example response
{
"key": "photos/a.jpg",
"size": 1024
}
| Status | Meaning |
|---|---|
200 | Stored. |
400 | Invalid request. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
411 | length_required. |
413 | Payload too large. |
429 | Plan quota or rate limit reached. Hard stop, never an overage charge. |
502 | storage_error; nothing was charged. |
DELETE /v1/objects/{key}
Delete an object. Scope: write.
Authentication: API key, scope or role write.
curl
curl -X DELETE "https://storage.seleid.dev/v1/objects/YOUR_KEY" \
-H "Authorization: Bearer $SELESTORAGE_KEY"TypeScript
const res = await fetch('https://storage.seleid.dev/v1/objects/YOUR_KEY', {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}` },
});
const data = await res.json();Python
import os, requests
res = requests.delete(
"https://storage.seleid.dev/v1/objects/YOUR_KEY",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"}
)
print(res.status_code, res.text)
Example response
{
"deleted": "photos/a.jpg"
}
| Status | Meaning |
|---|---|
200 | Deleted. |
400 | Invalid request. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
404 | Not found. |
429 | Plan quota or rate limit reached. Hard stop, never an overage charge. |
POST /v1/signed-urls
Mint a signed URL. Scope: read for GET, write for PUT. The returned url is relative to the service base and needs no API key. Lifetime defaults to 300 seconds, maximum 3600.
Authentication: API key, scope or role read.
curl
curl -X POST "https://storage.seleid.dev/v1/signed-urls" \
-H "Authorization: Bearer $SELESTORAGE_KEY" \
-H "Content-Type: application/json" \
-d '{"method":"PUT","key":"uploads/report.pdf","expires_in":300}'TypeScript
const res = await fetch('https://storage.seleid.dev/v1/signed-urls', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
"method": "PUT",
"key": "uploads/report.pdf",
"expires_in": 300
}),
});
const data = await res.json();Python
import os, requests
res = requests.post(
"https://storage.seleid.dev/v1/signed-urls",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"},
json={
"method": "PUT",
"key": "uploads/report.pdf",
"expires_in": 300,
}
)
print(res.status_code, res.text)
Example response
{
"url": "/v1/signed/uploads/report.pdf?tid=…&env=live&exp=1760000000&sig=…",
"method": "PUT",
"expires_at": "2026-11-12T12:05:00.000Z"
}
| Status | Meaning |
|---|---|
200 | Signed URL. |
400 | Invalid request. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
501 | signing_unavailable. |
GET /v1/signed/{key}
Download through a signed URL
No authentication.
curl
curl -X GET "https://storage.seleid.dev/v1/signed/YOUR_KEY"TypeScript
const res = await fetch('https://storage.seleid.dev/v1/signed/YOUR_KEY', {
method: 'GET',
});
const data = await res.json();Python
import os, requests
res = requests.get(
"https://storage.seleid.dev/v1/signed/YOUR_KEY"
)
print(res.status_code, res.text)
| Status | Meaning |
|---|---|
200 | Object bytes. |
400 | Invalid request. |
403 | Key lacks the required scope, or the account is suspended. |
404 | Not found. |
429 | Plan quota or rate limit reached. Hard stop, never an overage charge. |
PUT /v1/signed/{key}
Upload through a signed URL
No authentication.
curl
curl -X PUT "https://storage.seleid.dev/v1/signed/YOUR_KEY" \
--data-binary @fileTypeScript
const res = await fetch('https://storage.seleid.dev/v1/signed/YOUR_KEY', {
method: 'PUT',
body: await readFile('./file'),
});
const data = await res.json();Python
import os, requests
res = requests.put(
"https://storage.seleid.dev/v1/signed/YOUR_KEY",
data=open("file", "rb")
)
print(res.status_code, res.text)
| Status | Meaning |
|---|---|
200 | Stored. |
400 | Invalid request. |
403 | Key lacks the required scope, or the account is suspended. |
413 | Payload too large. |
429 | Plan quota or rate limit reached. Hard stop, never an overage charge. |
GET /v1/usage
Usage and limits for the current month. Scope: read.
Authentication: API key, scope or role read.
curl
curl -X GET "https://storage.seleid.dev/v1/usage" \
-H "Authorization: Bearer $SELESTORAGE_KEY"TypeScript
const res = await fetch('https://storage.seleid.dev/v1/usage', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}` },
});
const data = await res.json();Python
import os, requests
res = requests.get(
"https://storage.seleid.dev/v1/usage",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"}
)
print(res.status_code, res.text)
| Status | Meaning |
|---|---|
200 | Usage. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
POST /v1/keys
Create an API key. Scope: admin. The key is shown once.
Authentication: API key, scope or role admin.
curl
curl -X POST "https://storage.seleid.dev/v1/keys" \
-H "Authorization: Bearer $SELESTORAGE_KEY" \
-H "Content-Type: application/json" \
-d '{"scopes":["read","write"],"environment":"live"}'TypeScript
const res = await fetch('https://storage.seleid.dev/v1/keys', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
"scopes": [
"read",
"write"
],
"environment": "live"
}),
});
const data = await res.json();Python
import os, requests
res = requests.post(
"https://storage.seleid.dev/v1/keys",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"},
json={
"scopes": [
"read",
"write",
],
"environment": "live",
}
)
print(res.status_code, res.text)
Example response
{
"id": "key_abc",
"key": "ssk_live_…",
"scopes": [
"read",
"write"
],
"note": "Store this key now. It is shown only once."
}
| Status | Meaning |
|---|---|
201 | Created. |
400 | Invalid request. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
DELETE /v1/keys/{id}
Revoke a key. Scope: admin.
Authentication: API key, scope or role admin.
| Parameter | In | Required | Description |
|---|---|---|---|
id | path | yes | Key id. |
curl
curl -X DELETE "https://storage.seleid.dev/v1/keys/YOUR_ID" \
-H "Authorization: Bearer $SELESTORAGE_KEY"TypeScript
const res = await fetch('https://storage.seleid.dev/v1/keys/YOUR_ID', {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}` },
});
const data = await res.json();Python
import os, requests
res = requests.delete(
"https://storage.seleid.dev/v1/keys/YOUR_ID",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"}
)
print(res.status_code, res.text)
| Status | Meaning |
|---|---|
200 | Revoked. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
404 | Not found. |
POST /v1/keys/{id}/rotate
Rotate a key. Scope: admin. The old key is revoked.
Authentication: API key, scope or role admin.
| Parameter | In | Required | Description |
|---|---|---|---|
id | path | yes | Key id. |
curl
curl -X POST "https://storage.seleid.dev/v1/keys/YOUR_ID/rotate" \
-H "Authorization: Bearer $SELESTORAGE_KEY"TypeScript
const res = await fetch('https://storage.seleid.dev/v1/keys/YOUR_ID/rotate', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SELESTORAGE_KEY}` },
});
const data = await res.json();Python
import os, requests
res = requests.post(
"https://storage.seleid.dev/v1/keys/YOUR_ID/rotate",
headers={"Authorization": f"Bearer {os.environ['SELESTORAGE_KEY']}"}
)
print(res.status_code, res.text)
| Status | Meaning |
|---|---|
201 | Rotated. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
404 | Not found. |
Errors
| HTTP | Meaning |
|---|---|
400 | Invalid request. |
401 | Missing, invalid or revoked API key. |
403 | Key lacks the required scope, or the account is suspended. |
404 | Not found. |
413 | Payload too large. |
429 | Plan quota or rate limit reached. Hard stop, never an overage charge. |
The machine-readable OpenAPI 3.1 description for this API lives in the SeleID repository under docs/openapi/.