Cosmoner Docs
API Reference

Email templates

Stored, versioned emails that a send names instead of carrying its own subject and HTML. A template is written in the control panel, either as a React Email component in TSX or in the visual editor, and declares the variables a send supplies. Publishing freezes the draft into a numbered version; Send an email uses the published version unless it names another. [Email Templates](/docs/guides/email-templates) explains writing, previewing and publishing them. A template is addressed by its id or its alias wherever `{templateId}` appears. Aliases are lower-case letters, digits, `-` and `_`, unique in the project, and can be changed — the id never does. The calls here read templates, versions and folders, manage a template's name, alias, folder and sample data, roll back to an earlier version, and delete. Editing a template's content, publishing a version and sending a test are done in the control panel's editor, which builds what a publish stores. Templates themselves are free; the emails sent from them are billed like any other. A project can hold up to 200 templates and 100 folders, and each template keeps its 50 most recent versions.

GET
/v1/projects/{projectId}/email-templates

The project's templates, sorted by name, each with the version sends use. Filter by folder, or search by name or alias.

Auth: a project API key carrying email-templates:read.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length

Query Parameters

folderId?string

Only the templates directly in this folder, or root for those in no folder. Leave it out for every template.

Length1 <= length
q?string

Only templates whose name or alias contains this, ignoring case.

Lengthlength <= 128

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/projects/string/email-templates"
{  "success": true,  "data": [    {      "id": "string",      "name": "string",      "alias": "string",      "description": "string",      "editor": "CODE",      "folderId": "string",      "draftRevision": -9007199254740991,      "latestVersionNumber": -9007199254740991,      "createdAt": "2019-08-24T14:15:22Z",      "updatedAt": "2019-08-24T14:15:22Z",      "publishedVersion": {        "id": "string",        "version": -9007199254740991,        "createdAt": "2019-08-24T14:15:22Z"      }    }  ]}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
POST
/v1/projects/{projectId}/email-templates

Creates a template with an unpublished draft — a short example unless source gives another. Nothing can be sent from it until a version is published, which is done in the control panel's editor; until then a send naming it is refused with 409 EMAIL_TEMPLATE_NOT_PUBLISHED.

A project can hold up to 200 templates; one more is refused with 409 EMAIL_TEMPLATE_LIMIT. A code template's source is at most 256 KB and 50 files.

Members with the viewer role are refused, as is a project blocked over an unpaid invoice (403 ACCOUNT_BLOCKED).

Auth: a project API key carrying all of email-templates:read and email-templates:write.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length

Request 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 POST "https://example.com/v1/projects/string/email-templates" \  -H "Content-Type: application/json" \  -d '{    "name": "string",    "alias": "string",    "editor": "CODE"  }'
{  "success": true,  "data": {    "id": "string",    "name": "string",    "alias": "string",    "description": "string",    "editor": "CODE",    "folderId": "string",    "draftRevision": -9007199254740991,    "latestVersionNumber": -9007199254740991,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z",    "draftSource": null,    "previewData": {      "property1": null,      "property2": null    },    "publishedVersion": {      "id": "string",      "version": -9007199254740991,      "createdAt": "2019-08-24T14:15:22Z",      "variables": {        "property1": {          "type": "string",          "required": true,          "description": "string",          "sample": null,          "default": null        },        "property2": {          "type": "string",          "required": true,          "description": "string",          "sample": null,          "default": null        }      }    }  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
GET
/v1/projects/{projectId}/email-templates/{templateId}

One template, by id or alias, with its draft, its saved sample data and the variables its published version declares.

Auth: a project API key carrying email-templates:read.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length
templateId*string

The template's id or its alias.

Length1 <= length <= 64

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/projects/string/email-templates/string"
{  "success": true,  "data": {    "id": "string",    "name": "string",    "alias": "string",    "description": "string",    "editor": "CODE",    "folderId": "string",    "draftRevision": -9007199254740991,    "latestVersionNumber": -9007199254740991,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z",    "draftSource": null,    "previewData": {      "property1": null,      "property2": null    },    "publishedVersion": {      "id": "string",      "version": -9007199254740991,      "createdAt": "2019-08-24T14:15:22Z",      "variables": {        "property1": {          "type": "string",          "required": true,          "description": "string",          "sample": null,          "default": null        },        "property2": {          "type": "string",          "required": true,          "description": "string",          "sample": null,          "default": null        }      }    }  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
PATCH
/v1/projects/{projectId}/email-templates/{templateId}

Renames the template, changes its alias or description, moves it to another folder, or saves its sample data. Fields left out are unchanged. None of it changes what a send renders.

Changing the alias takes effect at once: sends naming the old alias are refused with 404 from then on, and sends by id are not affected. Sample data is at most 64 KB as JSON.

Members with the viewer role are refused, as is a project blocked over an unpaid invoice (403 ACCOUNT_BLOCKED).

Auth: a project API key carrying all of email-templates:read and email-templates:write.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length
templateId*string

The template's id or its alias.

Length1 <= length <= 64

Request 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/email-templates/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "success": true,  "data": {    "id": "string",    "name": "string",    "alias": "string",    "description": "string",    "editor": "CODE",    "folderId": "string",    "draftRevision": -9007199254740991,    "latestVersionNumber": -9007199254740991,    "createdAt": "2019-08-24T14:15:22Z",    "updatedAt": "2019-08-24T14:15:22Z",    "draftSource": null,    "previewData": {      "property1": null,      "property2": null    },    "publishedVersion": {      "id": "string",      "version": -9007199254740991,      "createdAt": "2019-08-24T14:15:22Z",      "variables": {        "property1": {          "type": "string",          "required": true,          "description": "string",          "sample": null,          "default": null        },        "property2": {          "type": "string",          "required": true,          "description": "string",          "sample": null,          "default": null        }      }    }  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
DELETE
/v1/projects/{projectId}/email-templates/{templateId}

Deletes the template, its draft and every version. Sends naming it are refused with 404 from then on. This cannot be undone.

Members with the viewer role are refused, as is a project blocked over an unpaid invoice (403 ACCOUNT_BLOCKED).

Auth: a project API key carrying all of email-templates:read and email-templates:write.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length
templateId*string

The template's id or its alias.

Length1 <= length <= 64

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X DELETE "https://example.com/v1/projects/string/email-templates/string"
{  "success": true,  "data": null}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
GET
/v1/projects/{projectId}/email-templates/{templateId}/versions

The template's published versions, newest first, with the one sends use marked published. The 50 most recent are kept; publishing past that removes the oldest, never the one sends use.

Auth: a project API key carrying email-templates:read.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length
templateId*string

The template's id or its alias.

Length1 <= length <= 64

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/projects/string/email-templates/string/versions"
{  "success": true,  "data": [    {      "id": "string",      "version": -9007199254740991,      "editor": "CODE",      "sourceBytes": -9007199254740991,      "compiledBytes": -9007199254740991,      "compilerVersion": "string",      "publishedByUserId": "string",      "createdAt": "2019-08-24T14:15:22Z",      "published": true    }  ]}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
POST
/v1/projects/{projectId}/email-templates/{templateId}/versions/{version}/promote

Makes an earlier version — or a later one, to roll forward again — the one sends use when they name no version. It takes effect on the next send. The draft is not touched, and the next publish still gets the next version number.

Members with the viewer role are refused, as is a project blocked over an unpaid invoice (403 ACCOUNT_BLOCKED).

Auth: a project API key carrying all of email-templates:read and email-templates:write.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length
templateId*string

The template's id or its alias.

Length1 <= length <= 64
version*integer

The version number.

Range0 < value <= 9007199254740991

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/v1/projects/string/email-templates/string/versions/1/promote"
{  "success": true,  "data": {    "id": "string",    "version": -9007199254740991,    "editor": "CODE",    "sourceBytes": -9007199254740991,    "compiledBytes": -9007199254740991,    "compilerVersion": "string",    "publishedByUserId": "string",    "createdAt": "2019-08-24T14:15:22Z",    "published": true  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
GET
/v1/projects/{projectId}/email-templates/folders

Every template folder in the project, sorted by name. Build the tree from each folder's parentId.

Auth: a project API key carrying email-templates:read.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/v1/projects/string/email-templates/folders"
{  "success": true,  "data": [    {      "id": "string",      "name": "string",      "parentId": "string"    }  ]}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
POST
/v1/projects/{projectId}/email-templates/folders

Creates a folder, at the top level or inside another. A project can hold up to 100 folders, nested at most 5 levels deep, and folders that share a parent need different names.

Members with the viewer role are refused, as is a project blocked over an unpaid invoice (403 ACCOUNT_BLOCKED).

Auth: a project API key carrying all of email-templates:read and email-templates:write.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length

Request 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

curl -X POST "https://example.com/v1/projects/string/email-templates/folders" \  -H "Content-Type: application/json" \  -d '{    "name": "string"  }'
{  "success": true,  "data": {    "id": "string",    "name": "string",    "parentId": "string"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
PATCH
/v1/projects/{projectId}/email-templates/folders/{folderId}

Renames a folder, moves it under another folder or to the top level, or both. What it holds moves with it.

Members with the viewer role are refused, as is a project blocked over an unpaid invoice (403 ACCOUNT_BLOCKED).

Auth: a project API key carrying all of email-templates:read and email-templates:write.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length
folderId*string
Length1 <= length

Request 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

curl -X PATCH "https://example.com/v1/projects/string/email-templates/folders/string" \  -H "Content-Type: application/json" \  -d '{}'
{  "success": true,  "data": {    "id": "string",    "name": "string",    "parentId": "string"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
DELETE
/v1/projects/{projectId}/email-templates/folders/{folderId}

Deletes the folder and nothing in it: its templates and subfolders move up to the folder's own parent. When a subfolder's name is already taken there, the delete is refused with the clashing names and nothing changes — rename or move that subfolder first.

Members with the viewer role are refused, as is a project blocked over an unpaid invoice (403 ACCOUNT_BLOCKED).

Auth: a project API key carrying all of email-templates:read and email-templates:write.

AuthorizationBearer <token>

A project API key. X-API-Key: <key> is accepted as an alternative to the Authorization header.

In: header

Path Parameters

projectId*string
Length1 <= length
folderId*string
Length1 <= length

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X DELETE "https://example.com/v1/projects/string/email-templates/folders/string"
{  "success": true,  "data": null}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}
{  "success": false,  "error": {    "code": "string",    "message": "string",    "details": {      "property1": null,      "property2": null    },    "docsUrl": "http://example.com"  }}

Email

Sending email from your own domains, over SMTP or through Send an email. Turn email on for a domain in the project, publish the DKIM, SPF and DMARC records it returns, and verify them; then create an SMTP credential for each sender address. [SMTP Setup](/docs/guides/smtp-setup) walks through it and has the server settings. Send an email takes its content inline or from a stored template; see [Email templates](/docs/api/email-templates) and the [Email Templates guide](/docs/guides/email-templates). Every email sent is billed on the project's subscription, each recipient counting as one email, and there is no free allowance. Projects start on pay as you go, with no monthly fee; a plan includes a number of emails each month for a flat price and bills the emails beyond it at a lower rate. Change the email plan moves between them. See [Email pricing](/docs/pricing/email). Sending is held under three ceilings, all shown by Get send limits. The **monthly cap** is your budget — the most the project will send, and pay for, in one quota month — and you set it. The **hourly** and **daily limits** are burst guards that stop a runaway loop from spending the month in an afternoon; they are raised on request, after review. Reaching any of them pauses sending. An hourly or daily pause lifts on its own as the window moves on; a monthly one lifts when the next quota month begins, or as soon as the cap is raised above what has already been sent. The project's biller is emailed whenever sending is paused. Adding a domain whose DNS is hosted elsewhere for email, and turning email off for a domain, are done in the control panel for now.

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.

On this page