Storage
Manage object storage buckets, IAM users, container registries, and repositories.
Object Storage
List Buckets
GET /v1/projects/:projectId/storage/object-storageReturns buckets with S3 endpoint info, including CDN status (cdnEnabled, cdnDomain).
Auth: Required (member, scope: storage:read)
Preview Cost
GET /v1/projects/:projectId/storage/object-storage/preview?provider=<provider>&tier=<tier>Returns a prorated cost preview for a bucket tier, including the subtotal, estimated tax, any applied credit, and the total due today. The tier parameter defaults to STARTER.
Available tiers: STARTER ($5/mo), GROWTH ($20/mo), SCALE ($75/mo), ENTERPRISE ($300/mo).
Auth: Required (member)
Create Bucket
POST /v1/projects/:projectId/storage/object-storageProvisions an S3-compatible bucket on a prepaid tier. The prorated amount is charged to the project's payment method off-session and the response is { deployed: true }. If the project has no card, the request returns 402 — see Billing → Deploy Payment Errors.
Request Body:
{
"name": "my-bucket",
"provider": "AWS_S3",
"region": "eu-north-1",
"tier": "STARTER",
"publicAccess": false,
"versioning": false,
"cdnEnabled": false,
"corsRules": [
{
"allowedOrigins": ["https://example.com"],
"allowedMethods": ["GET", "PUT"],
"allowedHeaders": ["*"],
"exposeHeaders": ["ETag"],
"maxAgeSeconds": 3600
}
]
}Each tier includes storage, egress, and request allowances. Usage is monitored and the bucket is suspended when the tier limit is exceeded by 10% (grace buffer). Upgrade to a higher tier to resume.
corsRules is optional. Each rule requires at least one allowedOrigins entry (use "*" to allow any origin) and one allowedMethods entry (GET, PUT, POST, DELETE, HEAD). allowedHeaders, exposeHeaders, and maxAgeSeconds are optional.
cdnEnabled is optional (defaults to false) and serves the bucket's public objects through a global CDN for faster delivery worldwide. It requires publicAccess: true; CloudFront delivery is metered at $0.12/GB and $0.015 per 10K requests. It can also be toggled after creation — see Update CDN below.
Auth: Required (member, write access, scope: storage:write)
Get Usage
GET /v1/projects/:projectId/storage/object-storage/:bucketId/usageReturns current storage, egress, and request usage for a bucket, along with the tier allowances. Usage is fetched from CloudWatch metrics (may have up to 24h delay).
Auth: Required (member, scope: storage:read)
Change Tier
PATCH /v1/projects/:projectId/storage/object-storage/:bucketId/tierChanges a bucket to a different tier. If the bucket was suspended due to exceeding its tier, upgrading reactivates it. Downgrades are allowed only when current usage fits within the target tier's limits.
Request Body:
{
"tier": "GROWTH"
}Auth: Required (member, write access, scope: storage:write)
Update CORS
PATCH /v1/projects/:projectId/storage/object-storage/:bucketId/corsReplaces the bucket's CORS (Cross-Origin Resource Sharing) configuration. The full rule set is applied at once — send the complete list, not a delta. An empty corsRules array clears the configuration.
Request Body:
{
"corsRules": [
{
"allowedOrigins": ["https://example.com", "https://app.example.com"],
"allowedMethods": ["GET", "PUT", "POST"],
"allowedHeaders": ["*"],
"exposeHeaders": ["ETag"],
"maxAgeSeconds": 3600
}
]
}Auth: Required (member, write access, scope: storage:write)
Update CDN
PATCH /v1/projects/:projectId/storage/object-storage/:bucketId/cdnEnables or disables the CDN add-on for a bucket. Enabling requires the bucket to already have publicAccess set to true — otherwise the request returns 400. Delivery is metered at $0.12/GB and $0.015 per 10K requests while the CDN is enabled.
Request Body:
{
"cdnEnabled": true
}Response Data:
{
"cdnEnabled": true,
"cdnDomain": "d1234abcd.cloudfront.net"
}cdnDomain is null while disabled or before the CDN has finished provisioning.
Auth: Required (member, write access, scope: storage:write)
Delete Bucket
DELETE /v1/projects/:projectId/storage/object-storage/:bucketIdDeletes the S3 bucket and removes the Stripe subscription item.
Auth: Required (member, write access, scope: storage:write)
List IAM Users
GET /v1/projects/:projectId/storage/object-storage/:bucketId/iam-usersAuth: Required (member)
Create IAM User
POST /v1/projects/:projectId/storage/object-storage/:bucketId/iam-usersCreates an IAM user with an access key and secret. Credentials are returned once and cannot be retrieved again.
Auth: Required (member, write access, scope: storage:write)
Delete IAM User
DELETE /v1/projects/:projectId/storage/object-storage/:bucketId/iam-users/:iamUserNameAuth: Required (member, write access, scope: storage:write)
Container Registry
List Registries
GET /v1/projects/:projectId/storage/container-registryReturns registries with their repositories.
Auth: Required (member, scope: storage:read)
Get Registry
GET /v1/projects/:projectId/storage/container-registry/:idAuth: Required (member)
List Available Regions
GET /v1/projects/:projectId/storage/container-registry/regionsReturns the regions a Cosmoner registry can be created in, as
{ "value": "eu-north-1", "label": "Europe (Stockholm)" } objects.
Auth: Required (member)
Preview Cost
GET /v1/projects/:projectId/storage/container-registry/previewReturns a prorated cost preview for the registry's $1/month fee, including the subtotal, estimated tax, any applied credit, and the total due today. Storage and egress are metered after the fact and are not part of the preview — see Container Registry for those rates.
Auth: Required (member)
Create Registry
POST /v1/projects/:projectId/storage/container-registryCreates a managed Cosmoner registry: the subscription items for its $1/month
fee, metered storage, and metered egress, plus the settings every repository
added under it inherits. region must be one of the values returned by the
regions endpoint and is fixed for the life of the registry.
The prorated fee is charged to the project's payment method off-session and the
response is { deployed: true, id }. If the project has no card, the request
returns 402 — see Billing → Deploy Payment Errors.
{
"name": "my-registry",
"region": "eu-north-1",
"imageTagMutability": "MUTABLE",
"scanOnPush": true,
"encryptionType": "AES256",
"lifecycleEnabled": true,
"lifecycleKeepLastN": 10,
"lifecycleUntaggedAfterDays": 7
}| Field | Default | Notes |
|---|---|---|
imageTagMutability | MUTABLE | IMMUTABLE rejects a second push to an existing tag. |
scanOnPush | true | Scans each pushed image for known vulnerabilities. |
encryptionType | AES256 | KMS requires kmsKeyArn and cannot be changed for existing repositories. |
kmsKeyArn | — | Required when encryptionType is KMS. |
lifecycleEnabled | true | Expires old images automatically. |
lifecycleKeepLastN | 10 | 1–1000. Images beyond this count expire. |
lifecycleUntaggedAfterDays | 7 | 1–365. Untagged images expire after this many days. |
Auth: Required (member, write access, scope: storage:write)
Update Registry
PATCH /v1/projects/:projectId/storage/container-registry/:idUpdates a registry's settings. At least one field must be provided. Tag mutability and scanning are re-applied to every existing repository; encryption applies only to repositories created afterwards, because it is fixed when a repository is created.
Auth: Required (member, write access, scope: storage:write)
Delete Registry
DELETE /v1/projects/:projectId/storage/container-registry/:idDeletes every repository under the registry and stops its fee, storage, and
egress charges. Billing is stopped before anything is deleted: if that fails
the registry is kept and the request returns 502, so the deletion can be
retried without losing track of what is still being charged.
Auth: Required (member, write access, scope: storage:write)
List Repositories
GET /v1/projects/:projectId/storage/container-registry/:id/repositoriesAuth: Required (member)
Create Repository
POST /v1/projects/:projectId/storage/container-registry/:id/repositoriesAuth: Required (member, write access, scope: storage:write)
Request Body:
{
"name": "my-app",
"visibility": "PRIVATE",
"publicEgressCapGb": null
}Names must be lowercase and may contain dots, hyphens, and underscores.
| Field | Default | Notes |
|---|---|---|
visibility | PRIVATE | PUBLIC allows anonymous pulls. Pushes always require an API key. |
publicEgressCapGb | — | Required when visibility is PUBLIC. 1–10,000 GB of anonymous egress per billing period; the repository reverts to PRIVATE when it is reached. |
Update Repository
PATCH /v1/projects/:projectId/storage/container-registry/:id/repositories/:repoIdUpdates the repository's retention policy. The policy is enforced by the registry itself, so expiry happens on the provider's schedule rather than at push time.
Auth: Required (member, write access, scope: storage:write)
Request Body:
{
"gcEnabled": true,
"gcKeepLastN": 10,
"gcDeleteUntagged": true,
"gcUntaggedAfterDays": 7,
"visibility": "PUBLIC",
"publicEgressCapGb": 50
}gcKeepLastN accepts 1–1000 and gcUntaggedAfterDays accepts 1–365. At least
one field must be provided.
visibility and publicEgressCapGb control anonymous pulls — see
Public repositories.
Switching to PUBLIC requires a cap, switching to PRIVATE clears it, and
either raising the cap or reopening a repository that hit it starts a fresh
allowance for the period.
Repositories are returned with publicEgressUsedGb, the anonymous egress
served in the current period, and publicEgressCapHitAt, set when a cap
closed the repository.
Delete Repository
DELETE /v1/projects/:projectId/storage/container-registry/:id/repositories/:repoIdAuth: Required (member, write access, scope: storage:write)