IAM
AWS access keys for tooling that cannot use a Cosmoner API key — `aws ecr get-login-password`, Kubernetes image pull secrets, S3 clients. These endpoints issue scoped credentials for those cases and list every credential a project has out there. Cosmoner API keys are a different thing and are not listed here; see [API Keys](/docs/guides/api-keys). One credential can reach the project's container registries, its object storage buckets, or both. A grant either names individual repositories and buckets or covers every one in the project, **including those created later**. Each credential's `origin` says which shape it has: `project` is the current one, and `registry` and `bucket` are older single-service shapes that keep working with the scope they were created with. The secret access key is returned once, when the credential is issued, and never stored. Pulls made with these credentials go directly to AWS and are not metered as egress; storage is billed as usual. An API key issuing or re-scoping a credential needs the scopes for what the credential grants as well as `iam:write`, so a key cannot hand out access to buckets or repositories it could not reach itself.
Every AWS credential the project has issued, newest first, as one list. A credential that
reaches both services is listed once, with a registry and a storage half.
A source that cannot be read is reported in errors instead of failing the request, so check
it before treating credentials as complete.
Auth: a project API key carrying iam:read.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= lengthResponse Body
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/projects/string/iam"{ "success": true, "data": { "credentials": [ { "iamUserName": "string", "label": "string", "accessKeyId": "string", "createdAt": "2019-08-24T14:15:22Z", "origin": "project", "registry": { "access": "pull", "allRepositories": true, "repositories": [ { "repositoryId": "string", "repositoryName": "string", "registryId": "string" } ] }, "storage": { "access": "read", "allBuckets": true, "buckets": [ { "bucketId": "string", "bucketName": "string" } ] } } ], "errors": [ "string" ] }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}Creates an AWS access key scoped to the repositories and buckets you name.
label (1–20 characters) must be unique within the project; it becomes part of the
credential's iamUserName and cannot be changed afterwards.
registry and storage each describe one half of the grant; send null, or leave one out,
for a credential that does not reach that service. At least one of them is required.
registry.access—pull(the default) is read-only;pushalso allows uploading images.registry.repositoryIds— the repositories the credential reaches. Empty (the default) means every repository in the project, including ones created later.storage.access—readallows downloading and listing;write(the default) also allows uploading and deleting.storage.bucketIds— the buckets the credential reaches. Empty (the default) means every bucket in the project, including ones created later.
Registry scoping stops at the repository: a credential can name individual repositories or cover the whole project, but not one registry within it.
The response carries secretAccessKey, which is returned once and never stored. A lost
secret cannot be recovered — delete the credential and issue a new one.
Members with the viewer role cannot issue, change or delete credentials.
With an API key, the key must also carry the scopes for what the credential grants: registry:read to pull, plus registry:write to push; object-storage:read to read, plus object-storage:write to write. A key cannot hand out access it does not hold itself — the call is refused with 403 INSUFFICIENT_SCOPE.
Auth: a project API key carrying all of iam:read and iam:write.
Rate limit: this endpoint is rate limited; a 429 means back off and retry.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= lengthRequest Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/projects/string/iam" \ -H "Content-Type: application/json" \ -d '{ "label": "string" }'{ "success": true, "data": { "iamUserName": "string", "label": "string", "accessKeyId": "string", "createdAt": "2019-08-24T14:15:22Z", "origin": "project", "registry": { "access": "pull", "allRepositories": true, "repositories": [ { "repositoryId": "string", "repositoryName": "string", "registryId": "string" } ] }, "storage": { "access": "read", "allBuckets": true, "buckets": [ { "bucketId": "string", "bucketName": "string" } ] }, "secretAccessKey": "string" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}One credential, described the same way the list describes it.
Auth: a project API key carrying iam:read.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= length1 <= lengthResponse Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X GET "https://example.com/v1/projects/string/iam/string"{ "success": true, "data": { "iamUserName": "string", "label": "string", "accessKeyId": "string", "createdAt": "2019-08-24T14:15:22Z", "origin": "project", "registry": { "access": "pull", "allRepositories": true, "repositories": [ { "repositoryId": "string", "repositoryName": "string", "registryId": "string" } ] }, "storage": { "access": "read", "allBuckets": true, "buckets": [ { "bucketId": "string", "bucketName": "string" } ] } }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}Rewrites what the credential reaches while keeping the same access key, so nothing using it has to be reissued.
Both halves are applied in full: a half sent as null, or left out, is removed from the
credential. Send every half you want it to keep.
registry and storage each describe one half of the grant; send null, or leave one out,
for a credential that does not reach that service. At least one of them is required.
registry.access—pull(the default) is read-only;pushalso allows uploading images.registry.repositoryIds— the repositories the credential reaches. Empty (the default) means every repository in the project, including ones created later.storage.access—readallows downloading and listing;write(the default) also allows uploading and deleting.storage.bucketIds— the buckets the credential reaches. Empty (the default) means every bucket in the project, including ones created later.
Registry scoping stops at the repository: a credential can name individual repositories or cover the whole project, but not one registry within it.
The label cannot be changed — it is part of the credential's name in AWS, so a different
label is a different credential. Only credentials with origin: "project" can be re-scoped;
the older shapes answer 400. A name that does not belong to this project is refused with
403 FORBIDDEN.
Members with the viewer role cannot issue, change or delete credentials.
With an API key, the key must also carry the scopes for what the credential grants: registry:read to pull, plus registry:write to push; object-storage:read to read, plus object-storage:write to write. A key cannot hand out access it does not hold itself — the call is refused with 403 INSUFFICIENT_SCOPE.
Auth: a project API key carrying all of iam:read and iam:write.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= length1 <= lengthRequest Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X PATCH "https://example.com/v1/projects/string/iam/string" \ -H "Content-Type: application/json" \ -d '{}'{ "success": true, "data": { "iamUserName": "string", "label": "string", "accessKeyId": "string", "createdAt": "2019-08-24T14:15:22Z", "origin": "project", "registry": { "access": "pull", "allRepositories": true, "repositories": [ { "repositoryId": "string", "repositoryName": "string", "registryId": "string" } ] }, "storage": { "access": "read", "allBuckets": true, "buckets": [ { "bucketId": "string", "bucketName": "string" } ] } }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}Deletes the credential and its access key, whichever origin it has. Anything using the key
stops working immediately.
A name that does not belong to this project is refused with 403 FORBIDDEN.
Members with the viewer role cannot issue, change or delete credentials.
Auth: a project API key carrying all of iam:read and iam:write.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= length1 <= lengthResponse Body
application/json
application/json
application/json
application/json
application/json
curl -X DELETE "https://example.com/v1/projects/string/iam/string"{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}The older registry-only shape, kept for callers written against it. Prefer
POST /v1/projects/{projectId}/iam, which can also reach object storage.
Creates a credential with origin: "registry", scoped to the one repository named by
repositoryId, or to every repository in the project when it is omitted. access defaults
to pull. The scope is fixed at creation: this credential can never be widened to object
storage or re-scoped — issue a new one instead.
The response carries secretAccessKey, which is returned once and never stored.
Members with the viewer role cannot issue, change or delete credentials.
With an API key, the key must also carry the scopes for what the credential grants: registry:read to pull, plus registry:write to push; object-storage:read to read, plus object-storage:write to write. A key cannot hand out access it does not hold itself — the call is refused with 403 INSUFFICIENT_SCOPE.
Auth: a project API key carrying all of iam:read and iam:write.
Rate limit: this endpoint is rate limited; a 429 means back off and retry.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= lengthRequest Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/v1/projects/string/iam/container-registry" \ -H "Content-Type: application/json" \ -d '{ "label": "string" }'{ "success": true, "data": { "iamUserName": "string", "label": "string", "access": "pull", "repositoryId": "string", "repositoryName": "string", "accessKeyId": "string", "secretAccessKey": "string", "createdAt": "2019-08-24T14:15:22Z" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}Deletes a credential with origin: "registry". Prefer
DELETE /v1/projects/{projectId}/iam/{iamUserName}, which deletes a credential of any origin.
A name that does not belong to this project is refused with 403 FORBIDDEN.
Members with the viewer role cannot issue, change or delete credentials.
Auth: a project API key carrying all of iam:read and iam:write.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= length1 <= lengthResponse Body
application/json
application/json
application/json
application/json
curl -X DELETE "https://example.com/v1/projects/string/iam/container-registry/string"{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}Deletes a credential with origin: "bucket" issued for the bucket bucketId. Prefer
DELETE /v1/projects/{projectId}/iam/{iamUserName}, which deletes a credential of any origin.
The bucket-only create endpoint this pairs with is likewise legacy and is not described
here; issue new credentials with Issue AWS credentials.
A name that does not belong to that bucket is refused with 403 FORBIDDEN.
Members with the viewer role cannot issue, change or delete credentials.
Auth: a project API key carrying all of iam:read and iam:write.
Authorization
apiKey A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.
In: header
Path Parameters
1 <= length1 <= length1 <= lengthResponse Body
application/json
application/json
application/json
application/json
application/json
application/json
curl -X DELETE "https://example.com/v1/projects/string/iam/object-storage/string/string"{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}{ "success": false, "error": { "code": "string", "message": "string", "details": { "property1": null, "property2": null }, "issues": null, "docsUrl": "http://example.com" }}Storage
Object storage buckets and container registries. **Buckets** are S3-compatible: any S3 client or SDK reads and writes them, with an access key issued under [IAM](/docs/api/iam). Each bucket runs on a prepaid tier that includes a storage, egress and request allowance, and its usage is watched against it — a bucket that goes more than 10% over any part of its allowance is suspended until it moves to a larger tier. See [Object Storage](/docs/guides/object-storage) and [Object storage pricing](/docs/pricing/object-storage). **Container registries** hold the images apps deploy from. Push and pull with `docker`, logging in with a Cosmoner API key that carries `registry:read` to pull and `registry:write` to push; see [Container Registry](/docs/guides/container-registry) and [Container registry pricing](/docs/pricing/container-registry). Repository names are unique across the project, not just within a registry. A repository is private by default. A public one can be pulled by anyone without credentials, and must carry a monthly cap on that anonymous egress (`publicEgressCapGb`, 1–10,000 GB), because it is billed to the project; when the cap is reached the repository becomes private again and `publicEgressCapHitAt` records when. Making it public again, or raising the cap, starts a fresh allowance. Several calls work with an API key but are not described operation by operation yet, because they answer with the resource's full internal record, which is not a stable contract: listing buckets (`GET /v1/projects/{projectId}/storage/object-storage`, `object-storage:read`), listing registries with their repositories (`GET /v1/projects/{projectId}/storage/container-registry`, `registry:read`), reading one (`GET …/container-registry/{id}`), listing and reading its repositories (`GET …/container-registry/{id}/repositories` and `…/repositories/{repoId}`), creating a repository (`POST …/container-registry/{id}/repositories` with `name` and optionally `visibility` and `publicEgressCapGb`, `registry:write`) and updating a repository's retention and visibility (`PATCH …/repositories/{repoId}`, `registry:write`). Rely only on `id`, `name`, `region`, `status`, `tier`, `publicAccess`, `publicPaths`, `versioning`, `corsRules`, `cdnEnabled`, `cdnDomain` and `createdAt` for a bucket; `id`, `name`, `region`, `status` and `createdAt` for a registry; and `id`, `name`, `fullPath`, `visibility`, `publicEgressCapGb`, `publicEgressUsedGb`, `publicEgressCapHitAt`, the `gc…` retention settings and `createdAt` for a repository — and expect the rest to change. Creating and updating a repository are refused to members with the viewer role, and to a project blocked over an unpaid invoice. Creating a bucket or a registry, previewing what one costs, changing a registry's settings, turning a bucket's CDN on or off, browsing a bucket's files and seeing which access keys reach a bucket or repository are done in the control panel for now.
Billing
How a project pays for what it runs. Each project has one payment method, a card, and every resource in it is billed to that card on the project's subscription: a prorated charge when the resource is created, then monthly on the 25th. See [Billing & Payments](/docs/guides/billing-and-payments) for the payment lifecycle and [Pricing](/docs/pricing) for what each product costs. **The biller.** One member holds billing responsibility for the project. Only the biller sees and manages the card, and only the biller can have the project's credit refunded; see [Transfer billing responsibility](/docs/api/members#transfer-billing-responsibility) to hand it to someone else. Any member with write access can create resources, and the charge always goes to the project's card, never to the member's own. **When a create is refused for payment.** A call that creates a billable resource charges the project's card before it answers, and is refused with `402` when it cannot, with nothing created. The `code` says why: `ORG_PAYMENT_METHOD_REQUIRED` — the project has no usable card and the caller is the biller, who adds one and retries; `BILLER_PAYMENT_METHOD_REQUIRED` — the project has no usable card and the caller is not the biller, and `error.details` names the biller (`billerUserId`, `billerName`, `billerEmail`) so they can be asked, which Notify the biller does; `ORG_BILLING_ADDRESS_REQUIRED` — the card's billing address is not complete enough to work out tax, so the card has to be added again with a full address; `PAYMENT_REQUIRED` — the card was declined, or the subscription is past due. A card that has expired, or is not the project's default, does not count as usable. **Credit.** Removing a resource part-way through a billing period credits the unused part to the project's credit balance, which is applied to its next invoices automatically. The biller can instead have the balance refunded to the card or cards that paid it. Payment-processing fees on the original charges cannot be recovered, so the amount refunded is less than the credit it uses up, and credit that cannot be returned to a card stays on the balance. The subscription breakdown, invoices, billing history, credit refunds and the card itself are managed under **Settings → Billing** in the control panel. Invoices and receipts are also emailed to the project's billing email, which Set the billing email changes.