SeleID

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/objects

List objects. Scope: read.

Authentication: API key, scope or role read.

ParameterInRequiredDescription
prefixquerynoKey prefix.
limitquerynoPage size.
cursorquerynoCursor 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
}
StatusMeaning
200A page.
400Invalid request.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
429Plan 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)
StatusMeaning
200Object bytes.
400Invalid request.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
404Not found.
429Plan 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 @file

TypeScript

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
}
StatusMeaning
200Stored.
400Invalid request.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
411length_required.
413Payload too large.
429Plan quota or rate limit reached. Hard stop, never an overage charge.
502storage_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"
}
StatusMeaning
200Deleted.
400Invalid request.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
404Not found.
429Plan 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"
}
StatusMeaning
200Signed URL.
400Invalid request.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
501signing_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)
StatusMeaning
200Object bytes.
400Invalid request.
403Key lacks the required scope, or the account is suspended.
404Not found.
429Plan 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 @file

TypeScript

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)
StatusMeaning
200Stored.
400Invalid request.
403Key lacks the required scope, or the account is suspended.
413Payload too large.
429Plan 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)
StatusMeaning
200Usage.
401Missing, invalid or revoked API key.
403Key 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."
}
StatusMeaning
201Created.
400Invalid request.
401Missing, invalid or revoked API key.
403Key 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.

ParameterInRequiredDescription
idpathyesKey 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)
StatusMeaning
200Revoked.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
404Not found.

POST /v1/keys/{id}/rotate

Rotate a key. Scope: admin. The old key is revoked.

Authentication: API key, scope or role admin.

ParameterInRequiredDescription
idpathyesKey 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)
StatusMeaning
201Rotated.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
404Not found.

Errors

HTTPMeaning
400Invalid request.
401Missing, invalid or revoked API key.
403Key lacks the required scope, or the account is suspended.
404Not found.
413Payload too large.
429Plan 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/.

Last updated 2026-11-12Suggest an editReport a problem with this page