{"openapi":"3.0.3","info":{"title":"Cosmoner API","version":"1.0.0","description":"The Cosmoner REST API.\n\nEvery successful response is wrapped in `{ \"success\": true, \"data\": … }`, and every\nfailure in `{ \"success\": false, \"error\": { \"code\", \"message\" } }`. Branch on\n`error.code`, which is stable; `error.message` is written for people and may change.\n\nAuthenticate with a project API key, sent as `Authorization: Bearer <key>` or\n`X-API-Key: <key>`. A key is scoped per resource and direction, so a key that may\nread servers cannot delete a domain.\n\nThis document is generated from the same schemas the API validates against, and is\nserved at `/v1/openapi.json`.","license":{"name":"Proprietary"}},"servers":[{"url":"https://api.cosmoner.com"}],"tags":[{"name":"Projects","x-displayName":"Projects","description":"A project is the unit of ownership and billing: every resource belongs to exactly one, and an API key reaches exactly one. Address a project by its `id` or its `slug` — both are accepted wherever `{projectId}` appears."},{"name":"Redis","x-displayName":"Redis","description":"Managed Redis databases. A database's password is returned only by the single-database read, never by the list, so enumerating databases does not hand out credentials for all of them."},{"name":"SSH keys","x-displayName":"SSH keys","description":"Public keys that authorise access to the servers in a project. A key is written to a server by cloud-init at first boot, so it must be registered *before* the server that should trust it is created."},{"name":"Secrets","x-displayName":"Secrets","description":"Encrypted configuration. A secret's value is returned exactly once — by the call that sets it — and never again; reads describe the secret without handing the plaintext back. For values that do not need protecting, use variables instead."},{"name":"Servers","x-displayName":"Servers","description":"Dedicated machines in a project. The keys that can log into a server are fixed when it is created — they are written to the machine at first boot — so decide them before provisioning, not after."},{"name":"Variables","x-displayName":"Variables","description":"Non-sensitive configuration, stored and returned in plaintext and scoped to a deployment environment. For values that must not be readable, use secrets instead."},{"name":"Webhooks","x-displayName":"Webhooks","description":"Subscriptions to platform events. Each endpoint has a signing secret, returned once when it is created or rotated, that signs every delivery so you can verify it came from us. Repeated failures pause an endpoint automatically; resuming it re-queues what was held."}],"paths":{"/v1/projects":{"get":{"operationId":"listProjects","summary":"List projects","description":"Every project the authenticated user is a member of, newest first.\n\n**Auth**: a project API key carrying `projects:read`.","tags":["Projects"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"The caller's projects.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjects200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `projects:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}":{"get":{"operationId":"getProject","summary":"Fetch a project","description":"The project, with a count of the resources it holds.\n\n**Auth**: a project API key carrying `projects:read`.","tags":["Projects"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetProject200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `projects:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such project, or you are not a member of it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateProject","summary":"Rename a project","description":"**Auth**: a project API key carrying all of `projects:read` and `projects:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Projects"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100}},"required":["name"]}}}},"responses":{"200":{"description":"The updated project settings.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProject200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `projects:read` and `projects:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"You already have a project with that name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteProject","summary":"Delete a project","description":"Deletes the project. Every resource it holds must be removed first — servers, domains,\napps, buckets, registries and databases. While any remain the call returns `409 CONFLICT`\nlisting what is left.\n\nDeletion is immediate and cannot be undone.\n\n**Auth**: a project API key carrying all of `projects:read` and `projects:write`.\n\n**Role**: the caller must be the project owner.","tags":["Projects"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The project was deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteProject200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `projects:read` and `projects:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such project, or you do not own it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The project still holds resources.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/billing-email":{"patch":{"operationId":"updateProjectBillingEmail","summary":"Set the billing email","description":"Sets the address invoices and receipts are sent to. Pass `null` to clear it, which falls\nback to the account email of the member who holds billing for the project.\n\n**Auth**: a project API key carrying all of `projects:read` and `projects:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Projects"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"billingEmail":{"nullable":true,"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}},"required":["billingEmail"]}}}},"responses":{"200":{"description":"The address now in effect.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProjectBillingEmail200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `projects:read` and `projects:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/redis":{"get":{"operationId":"listRedisDatabases","summary":"List Redis databases","description":"Every Redis database in the project, newest first. Terminated databases are omitted.\n\nPasswords are never included here — read a single database to get its credential.\n\n**Auth**: a project API key carrying `redis:read`.","tags":["Redis"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The project's Redis databases.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListRedisDatabases200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `redis:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createRedisDatabase","summary":"Provision a Redis database","description":"Provisions a managed Redis database and adds it to the project's subscription.\n\n`replication` reserves half the plan's memory for a replica and bills a second unit of the\nplan price, so a 1 GB plan with replication gives 512 MB of usable memory.\n\nBilling is charged to the project's saved card. A project with no usable card returns `402`.\n\n**Auth**: a project API key carrying all of `redis:read` and `redis:write`.","tags":["Redis"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":40,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"},"planSlug":{"type":"string","minLength":1},"region":{"type":"string","minLength":1},"replication":{"default":false,"type":"boolean"},"dataPersistence":{"default":"NONE","type":"string","enum":["NONE","AOF_EVERY_WRITE","AOF_EVERY_1_SECOND","SNAPSHOT_EVERY_1_HOUR","SNAPSHOT_EVERY_6_HOURS","SNAPSHOT_EVERY_12_HOURS"]}},"required":["name","planSlug","region"]}}}},"responses":{"200":{"description":"Provisioning has started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRedisDatabase200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The project has no card that can be charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `redis:read` and `redis:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The database could not be provisioned upstream.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/redis/{redisId}":{"get":{"operationId":"getRedisDatabase","summary":"Fetch a Redis database","description":"The database, together with the password for its default user — everything needed to connect.\n\nThis is the only endpoint that returns the password; the list deliberately omits it.\n\n**Auth**: a project API key carrying `redis:read`.","tags":["Redis"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"redisId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The database and its credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRedisDatabase200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `redis:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such database in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteRedisDatabase","summary":"Delete a Redis database","description":"Deletes the database and stops billing for it. The data is not recoverable.\n\n**Auth**: a project API key carrying all of `redis:read` and `redis:write`.","tags":["Redis"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"redisId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The database was deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteRedisDatabase200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `redis:read` and `redis:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such database in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/redis/{redisId}/usage":{"get":{"operationId":"getRedisUsage","summary":"Fetch Redis usage","description":"Memory and network usage for the database.\n\nA database that is still provisioning reports zeroes rather than failing, so a caller can\nrender the same view either way. Figures are cached briefly and may lag by up to a minute.\n\n**Auth**: a project API key carrying `redis:read`.","tags":["Redis"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"redisId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Current usage.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRedisUsage200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `redis:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such database in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/redis/preview":{"get":{"operationId":"previewRedisCost","summary":"Preview what a Redis database will cost","description":"What a given plan would be charged today and each month after, before committing to it.\n\n**Auth**: a project API key carrying `redis:read`.","tags":["Redis"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"planSlug","in":"query","required":true,"description":"The plan to price, as listed by the catalogue.","schema":{"type":"string","minLength":1}},{"name":"replication","in":"query","required":false,"description":"Price with high availability, which bills a second unit of the plan.","schema":{"type":"string","enum":["true","false"]}}],"responses":{"200":{"description":"The cost preview.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewRedisCost200Response"}}}},"400":{"description":"`planSlug` is required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `redis:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/secrets":{"get":{"operationId":"listSecrets","summary":"List secrets","description":"Metadata for every secret on the project — never the values. A secret's plaintext is\nreturned only by the call that sets it.\n\nPass `environment` to narrow the list. Secrets in `default` apply to every environment\nunless an entry with the same name exists for that environment.\n\n**Auth**: a project API key carrying `secrets:read`.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"environment","in":"query","required":false,"schema":{"type":"string","enum":["default","development","staging","production"]}}],"responses":{"200":{"description":"The project's secrets.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSecrets200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `secrets:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createSecret","summary":"Create a secret","description":"Stores an encrypted value and returns the plaintext once, in this response.\n\nNames are unique per environment. A project that has reached its secret limit returns\n`402 PAYMENT_REQUIRED`; add capacity with the upgrade endpoint.\n\n**Auth**: a project API key carrying all of `secrets:read` and `secrets:write`.\n\n**Role**: the caller must be an owner or admin of the project.\n\n**Rate limit**: this endpoint is rate limited; a `429` means back off and retry.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[A-Z][A-Z0-9_]*$"},"value":{"type":"string","minLength":1,"maxLength":10000},"description":{"type":"string","maxLength":500},"environment":{"type":"string","enum":["default","development","staging","production"]}},"required":["name","value"]}}}},"responses":{"201":{"description":"The created secret, with its plaintext value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSecret201Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The project has reached its secret limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `secrets:read` and `secrets:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A secret with that name already exists in the environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many requests. Back off and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/secrets/{secretId}":{"get":{"operationId":"getSecret","summary":"Fetch a secret","description":"The secret's metadata. The value is not included and cannot be read back.\n\n**Auth**: a project API key carrying `secrets:read`.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"secretId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSecret200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `secrets:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such secret in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateSecret","summary":"Replace a secret's value","description":"Re-encrypts the secret with a new value and increments its `version`. The new plaintext is\nreturned once, in this response.\n\n**Auth**: a project API key carrying all of `secrets:read` and `secrets:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"secretId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":10000},"description":{"type":"string","maxLength":500}},"required":["value"]}}}},"responses":{"200":{"description":"The updated secret, with its new plaintext value.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSecret200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `secrets:read` and `secrets:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such secret in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteSecret","summary":"Delete a secret","description":"**Auth**: a project API key carrying all of `secrets:read` and `secrets:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"secretId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"204":{"description":"The secret was deleted. No body is returned."},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `secrets:read` and `secrets:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such secret in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/secrets/{secretId}/audit":{"get":{"operationId":"getSecretAudit","summary":"Fetch a secret's audit trail","description":"Who created, changed or deleted the secret, and when. Values never appear here.\n\n**Auth**: a project API key carrying `secrets:read`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"secretId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The audit trail, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSecretAudit200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `secrets:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such secret in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/secrets/upgrade":{"post":{"operationId":"upgradeSecrets","summary":"Buy secret capacity","description":"Adds secrets packs to the project's subscription, raising the limit. Defaults to one pack.\n\nThe change is billed from the moment it takes effect; the response is the capacity now in\nforce.\n\n**Auth**: a project API key carrying all of `secrets:read` and `secrets:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"default":{},"type":"object","properties":{"packs":{"default":1,"type":"integer","minimum":1,"maximum":20}}}}}},"responses":{"200":{"description":"Capacity after the purchase.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeSecrets200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `secrets:read` and `secrets:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/secrets/usage":{"get":{"operationId":"getSecretsUsage","summary":"Fetch secret capacity","description":"How many secrets the project stores, how many it may store, and the price of more.\n\n**Auth**: a project API key carrying `secrets:read`.","tags":["Secrets"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Current usage and limits.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSecretsUsage200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `secrets:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/servers":{"get":{"operationId":"listServers","summary":"List servers","description":"Every server in the project, newest first. Terminated servers are omitted.\n\nEach server's status and public address are reconciled with the platform as part of this\nread, so a machine that has finished starting reports `RUNNING` here first.\n\n**Auth**: a project API key carrying `servers:read`.","tags":["Servers"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The project's servers.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServers200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `servers:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createServer","summary":"Provision a server","description":"Provisions a server and adds it to the project's subscription.\n\n`sshKeyIds` is the only chance to decide who can log in: the keys are written to the machine\nwhen it first boots, and none can be added afterwards. A server created without any cannot\nbe reached at all, short of rebuilding it.\n\nBilling is charged to the project's saved card. A project with no usable card returns `402`.\nProvisioning is transactional — a failure part-way rolls back what it created.\n\n**Auth**: a project API key carrying all of `servers:read` and `servers:write`.","tags":["Servers"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"},"slug":{"type":"string","minLength":1},"provider":{"type":"string","enum":["digitalocean"]},"region":{"type":"string","minLength":1},"template":{"type":"string"},"sshKeyIds":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1}}},"required":["name","slug","provider","region"]}}}},"responses":{"200":{"description":"Provisioning has started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServer200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"The project has no card that can be charged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `servers:read` and `servers:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The server could not be provisioned upstream.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/servers/{id}":{"get":{"operationId":"getServer","summary":"Fetch a server","description":"The server, with the keys authorised on it — enough to build the connect instructions, naming the right user and key.\n\n**Auth**: a project API key carrying `servers:read`.","tags":["Servers"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetServer200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `servers:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such server in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateServer","summary":"Rename a server","description":"Renames the machine upstream first, so a failure leaves both sides on the old name rather than disagreeing.\n\n**Auth**: a project API key carrying all of `servers:read` and `servers:write`.","tags":["Servers"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":63,"pattern":"^[a-z0-9]([a-z0-9-]*[a-z0-9])?$"}},"required":["name"]}}}},"responses":{"200":{"description":"The renamed server.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateServer200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `servers:read` and `servers:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such server in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteServer","summary":"Destroy a server","description":"Destroys the machine and stops billing for it. The data on it is not recoverable.\n\nIf the call fails part-way the server stays visible and the call can be retried — it is never\nleft billing invisibly.\n\n**Auth**: a project API key carrying all of `servers:read` and `servers:write`.","tags":["Servers"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The server was destroyed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteServer200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `servers:read` and `servers:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such server in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/servers/{id}/actions":{"post":{"operationId":"runServerAction","summary":"Power a server on, off, or reboot it","description":"Power actions are applied asynchronously, so the server comes back as `PROVISIONING` — the\nstate it sits in while the action settles. Read the server again to see where it lands.\n\nReturns `409` if the action does not fit the current state: powering on a running server,\nor stopping one that is not running.\n\n**Auth**: a project API key carrying all of `servers:read` and `servers:write`.","tags":["Servers"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["power_on","power_off","reboot"]}},"required":["action"]}}}},"responses":{"200":{"description":"The action was requested.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunServerAction200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `servers:read` and `servers:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such server in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The server is still provisioning, or is already in that state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/servers/preview":{"get":{"operationId":"previewServerCost","summary":"Preview what a server will cost","description":"What provisioning a given size would be charged today and each month after, before committing to it.\n\n**Auth**: a project API key carrying `servers:read`.","tags":["Servers"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"provider","in":"query","required":true,"schema":{"type":"string","enum":["digitalocean"]}},{"name":"slug","in":"query","required":true,"description":"The size to price, as listed by the catalogue.","schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The cost preview.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewServerCost200Response"}}}},"400":{"description":"`provider` and `slug` are both required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `servers:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/ssh-keys":{"get":{"operationId":"listSshKeys","summary":"List SSH keys","description":"Every SSH key registered on the project, newest first.\n\n**Auth**: a project API key carrying `servers:read`.","tags":["SSH keys"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The project's SSH keys.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSshKeys200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `servers:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createSshKey","summary":"Register an SSH key","description":"Registers a public key you already hold, so it can authorise servers created afterwards.\n\nKeys are deduplicated by fingerprint within a project: registering one that is already\npresent returns `409 CONFLICT` naming the existing key rather than creating a second entry.\n\n**Auth**: a project API key carrying all of `servers:read` and `servers:write`.","tags":["SSH keys"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"publicKey":{"type":"string","minLength":1,"maxLength":16384,"pattern":"^(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp(256|384|521)|sk-ssh-ed25519@openssh\\.com|sk-ecdsa-sha2-nistp256@openssh\\.com) [A-Za-z0-9+/]+={0,3}( .*)?$"}},"required":["name","publicKey"]}}}},"responses":{"201":{"description":"The registered key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSshKey201Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `servers:read` and `servers:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A key with the same fingerprint is already registered.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/ssh-keys/{sshKeyId}":{"delete":{"operationId":"deleteSshKey","summary":"Remove an SSH key","description":"Removes the key from the project so it no longer authorises new servers.\n\nServers already provisioned with it keep accepting it: the key was written to disk at first\nboot and is not reached back into. `stillAuthorisedOn` says how many such servers remain.\n\n**Auth**: a project API key carrying all of `servers:read` and `servers:write`.","tags":["SSH keys"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"sshKeyId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The key was removed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteSshKey200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `servers:read` and `servers:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such key in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/ssh-keys/generate":{"post":{"operationId":"generateSshKey","summary":"Generate an SSH keypair","description":"Generates a keypair on your behalf and registers the public half.\n\nThe private key is returned in this response and nowhere else — it is never stored and\nnever logged. If you lose it, the only recovery is to generate a new key and rebuild the\nservers that trusted the old one.\n\n**Auth**: a project API key carrying all of `servers:read` and `servers:write`.\n\n**Rate limit**: this endpoint is rate limited; a `429` means back off and retry.","tags":["SSH keys"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100}},"required":["name"]}}}},"responses":{"201":{"description":"The registered key, together with the private half.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateSshKey201Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `servers:read` and `servers:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Too many requests. Back off and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/stats":{"get":{"operationId":"getProjectStats","summary":"Fetch spend by resource type","description":"What the project was charged per resource type over the last five closed months, plus the\nmonth still accruing — that one is marked `inProgress` and is a running total, not a\nsettled invoice.\n\n**Auth**: a project API key carrying `projects:read`.","tags":["Projects"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"Spend by month.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetProjectStats200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `projects:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such project, or you are not a member of it.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/variables":{"get":{"operationId":"listVariables","summary":"List variables","description":"Every variable on the project, oldest first, with values included — variables hold\nnon-sensitive configuration and are stored in plaintext. Use secrets for anything that\nmust not be readable.\n\nPass `environment` to narrow the list to one environment. Variables in `default` apply to\nevery environment unless an entry with the same name exists for that environment.\n\n**Auth**: a project API key carrying `variables:read`.","tags":["Variables"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"environment","in":"query","required":false,"schema":{"type":"string","enum":["default","development","staging","production"]}}],"responses":{"200":{"description":"The project's variables.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListVariables200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `variables:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createVariable","summary":"Create a variable","description":"Names are unique per environment, so the same name may exist once in `default` and once in\n`production`. A name already present in the target environment returns `409 CONFLICT`.\n\n**Auth**: a project API key carrying all of `variables:read` and `variables:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Variables"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[A-Z][A-Z0-9_]*$"},"value":{"type":"string","minLength":1,"maxLength":10000},"description":{"type":"string","maxLength":500},"environment":{"type":"string","enum":["default","development","staging","production"]}},"required":["name","value"]}}}},"responses":{"201":{"description":"The created variable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVariable201Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `variables:read` and `variables:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A variable with that name already exists in the environment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/variables/{variableId}":{"get":{"operationId":"getVariable","summary":"Fetch a variable","description":"**Auth**: a project API key carrying `variables:read`.","tags":["Variables"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"variableId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The variable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetVariable200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `variables:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such variable in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateVariable","summary":"Update a variable","description":"Changes the value, the description, or both. Send at least one of them; a body with neither is rejected.\n\n**Auth**: a project API key carrying all of `variables:read` and `variables:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Variables"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"variableId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":10000},"description":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"The updated variable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateVariable200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `variables:read` and `variables:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such variable in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteVariable","summary":"Delete a variable","description":"**Auth**: a project API key carrying all of `variables:read` and `variables:write`.\n\n**Role**: the caller must be an owner or admin of the project.","tags":["Variables"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"variableId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"204":{"description":"The variable was deleted. No body is returned."},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `variables:read` and `variables:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such variable in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/webhooks":{"get":{"operationId":"listWebhookEndpoints","summary":"List webhook endpoints","description":"Every endpoint on the project, newest first.\n\n**Auth**: a project API key carrying `webhooks:read`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The project's endpoints.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookEndpoints200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `webhooks:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createWebhookEndpoint","summary":"Create a webhook endpoint","description":"Subscribes a URL to one or more events.\n\nThe signing secret is returned in this response and nowhere else. Use it to verify that a\ndelivery came from us; if you lose it, rotate it rather than trying to read it back.\n\n**Auth**: a project API key carrying all of `webhooks:read` and `webhooks:write`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":500},"url":{"type":"string","format":"uri"},"events":{"minItems":1,"type":"array","items":{"type":"string","enum":["email.sent","email.delivered","email.delivery_delayed","email.bounced","email.complained","email.opened","email.clicked","email.rejected","email.rendering_failed","email.domain_verified","email.sending_paused","app.deployed","app.failed","domain.verified","domain.expired","server.running","server.error","member.invited","member.joined"]}}},"required":["name","url","events"]}}}},"responses":{"201":{"description":"The created endpoint, with its signing secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookEndpoint201Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `webhooks:read` and `webhooks:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/webhooks/{endpointId}":{"get":{"operationId":"getWebhookEndpoint","summary":"Fetch a webhook endpoint","description":"The endpoint, with a count of its deliveries by status.\n\n**Auth**: a project API key carrying `webhooks:read`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The endpoint and its delivery counts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetWebhookEndpoint200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `webhooks:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateWebhookEndpoint","summary":"Update a webhook endpoint","description":"Changes the name, description, URL, subscribed events, or whether the endpoint is enabled.\n\nSwitching a disabled endpoint back on also clears its failure streak — otherwise the next\nsingle failure would pause it again.\n\n**Auth**: a project API key carrying all of `webhooks:read` and `webhooks:write`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"description":{"nullable":true,"type":"string","maxLength":500},"url":{"type":"string","format":"uri"},"events":{"minItems":1,"type":"array","items":{"type":"string","enum":["email.sent","email.delivered","email.delivery_delayed","email.bounced","email.complained","email.opened","email.clicked","email.rejected","email.rendering_failed","email.domain_verified","email.sending_paused","app.deployed","app.failed","domain.verified","domain.expired","server.running","server.error","member.invited","member.joined"]}},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"The updated endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookEndpoint200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `webhooks:read` and `webhooks:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteWebhookEndpoint","summary":"Delete a webhook endpoint","description":"Deletes the endpoint and its delivery history.\n\n**Auth**: a project API key carrying all of `webhooks:read` and `webhooks:write`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The endpoint was deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteWebhookEndpoint200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `webhooks:read` and `webhooks:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/webhooks/{endpointId}/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"List deliveries","description":"The endpoint's delivery log, newest first, with what the endpoint answered on each attempt.\n\nPaginated by cursor: pass the `nextCursor` from one response as `cursor` on the next. A\n`nextCursor` of `null` means there are no further pages. Narrow the log with `status` and\n`eventType`.\n\n**Auth**: a project API key carrying `webhooks:read`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["PENDING","SUCCEEDED","FAILED"]}},{"name":"eventType","in":"query","required":false,"schema":{"type":"string","enum":["email.sent","email.delivered","email.delivery_delayed","email.bounced","email.complained","email.opened","email.clicked","email.rejected","email.rendering_failed","email.domain_verified","email.sending_paused","app.deployed","app.failed","domain.verified","domain.expired","server.running","server.error","member.invited","member.joined"]}},{"name":"limit","in":"query","required":false,"schema":{"default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"A page of deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListWebhookDeliveries200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `webhooks:read`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/webhooks/{endpointId}/deliveries/{deliveryId}/replay":{"post":{"operationId":"replayWebhookDelivery","summary":"Replay a delivery","description":"Queues the same event for delivery again, as a new attempt with its own row. The original\ndelivery is left as it was.\n\nThe endpoint must be enabled — replaying into a paused endpoint returns `400`.\n\n**Auth**: a project API key carrying all of `webhooks:read` and `webhooks:write`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"201":{"description":"The queued replay.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplayWebhookDelivery201Response"}}}},"400":{"description":"The endpoint is paused. Resume it first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `webhooks:read` and `webhooks:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint or delivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/webhooks/{endpointId}/resume":{"post":{"operationId":"resumeWebhookEndpoint","summary":"Resume a paused endpoint","description":"Clears an automatic pause and re-queues the events held while the endpoint was down.\n\nAn endpoint is paused automatically after enough consecutive failures; resuming resets that\nstreak.\n\n**Auth**: a project API key carrying all of `webhooks:read` and `webhooks:write`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The resumed endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeWebhookEndpoint200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `webhooks:read` and `webhooks:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/webhooks/{endpointId}/rotate-secret":{"post":{"operationId":"rotateWebhookSecret","summary":"Rotate the signing secret","description":"Issues a new signing secret and returns it once. The previous secret stops verifying\nimmediately, so deploy the new one before rotating if you cannot tolerate a gap.\n\n**Auth**: a project API key carrying all of `webhooks:read` and `webhooks:write`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"The new signing secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateWebhookSecret200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `webhooks:read` and `webhooks:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/{projectId}/webhooks/{endpointId}/test":{"post":{"operationId":"testWebhookEndpoint","summary":"Send a test event","description":"Fires a synthetic event at the endpoint immediately and returns what it answered, so a\nmisconfigured URL is caught at setup rather than on the first real event.\n\nA test never moves the failure streak in either direction: an unreachable endpoint is what\nyou are checking for, and a test that succeeds says nothing about real events still failing.\n\n**Auth**: a project API key carrying all of `webhooks:read` and `webhooks:write`.","tags":["Webhooks"],"security":[{"apiKey":[]}],"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"endpointId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"eventType":{"type":"string","enum":["email.sent","email.delivered","email.delivery_delayed","email.bounced","email.complained","email.opened","email.clicked","email.rejected","email.rendering_failed","email.domain_verified","email.sending_paused","app.deployed","app.failed","domain.verified","domain.expired","server.running","server.error","member.invited","member.joined"]}}}}}},"responses":{"200":{"description":"The attempt and its outcome.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestWebhookEndpoint200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry all of `webhooks:read` and `webhooks:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No such endpoint in this project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects/create":{"post":{"operationId":"createProject","summary":"Create a project","description":"Creates a project and the infrastructure grouping behind it.\n\nProject names are unique per user, case-insensitively: reusing one returns `409 CONFLICT`.\nCreation is transactional — if any step fails, everything it created is rolled back.\n\n**Auth**: a project API key carrying `projects:write`.","tags":["Projects"],"security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100}},"required":["name"]}}}},"responses":{"200":{"description":"Identifiers for the new project.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProject200Response"}}}},"401":{"description":"No credential was presented, or the API key is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The caller is not a member of the project, or the API key does not carry `projects:write`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"You already have a project with that name.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"The request body, path or query parameters failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"The infrastructure provider could not be reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"A project API key. `X-API-Key: <key>` is accepted as an alternative to the `Authorization` header."}},"schemas":{"ProjectEnvironment":{"type":"string","enum":["default","development","staging","production"]},"WebhookEventType":{"type":"string","enum":["email.sent","email.delivered","email.delivery_delayed","email.bounced","email.complained","email.opened","email.clicked","email.rejected","email.rendering_failed","email.domain_verified","email.sending_paused","app.deployed","app.failed","domain.verified","domain.expired","server.running","server.error","member.invited","member.joined"]},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"object","properties":{"code":{"type":"string","description":"Machine-readable and stable — branch on this, not on the message. For example `NOT_FOUND`, `INSUFFICIENT_SCOPE`, `VALIDATION_ERROR`."},"message":{"type":"string","description":"Human-readable explanation. Wording may change between releases."}},"required":["code","message"]}},"required":["success","error"],"description":"The body every failure shares, whether it came from a route or from request validation."},"Actor":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}},"required":["id","name","email"],"description":"A member of the project, as embedded in the audit fields of resources that record who touched them. Carries only the identity a fellow member can already see on the members page."},"ProjectResourceCounts":{"type":"object","properties":{"servers":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"domains":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"members":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"apps":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"objectStorages":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"containerRegistries":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"databaseClusters":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["servers","domains","members","apps","objectStorages","containerRegistries","databaseClusters"],"description":"How many of each resource the project holds, so a client can render an overview without a request per resource type."},"Project":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","description":"Stable URL-safe identifier. Accepted anywhere `{projectId}` appears, so either this or `id` may be used to address the project."},"billingEmail":{"nullable":true,"description":"Address invoices and receipts are sent to. `null` falls back to the account email of the member who holds billing for the project.","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"blockedAt":{"nullable":true,"description":"Set when the project is blocked; `null` while it is in good standing.","type":"string","format":"date-time"},"blockedReason":{"nullable":true,"description":"Why the project was blocked; `null` while it is in good standing.","type":"string"},"_count":{"$ref":"#/components/schemas/ProjectResourceCounts"}},"required":["id","name","slug","billingEmail","blockedAt","blockedReason","_count"],"description":"A project."},"ProjectSettings":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"billingEmail":{"nullable":true,"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}},"required":["id","name","slug","billingEmail"],"description":"The subset echoed back after a rename — deliberately narrower than `Project`: only the fields the update could have changed."},"ProjectBillingEmail":{"type":"object","properties":{"billingEmail":{"nullable":true,"type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}},"required":["billingEmail"],"description":"The result of setting or clearing the billing email."},"CreatedProject":{"type":"object","properties":{"projectId":{"type":"string"},"projectSlug":{"type":"string","description":"Use this or `projectId` as `{projectId}` on subsequent requests."}},"required":["projectId","projectSlug"],"description":"Identifiers for a freshly created project."},"ResourceHistoryMonth":{"type":"object","properties":{"month":{"type":"string","description":"Abbreviated month name, e.g. `Mar`."},"servers":{"type":"number"},"domains":{"type":"number"},"apps":{"type":"number"},"objectStorage":{"type":"number"},"containerRegistry":{"type":"number"},"databases":{"type":"number"},"redis":{"type":"number"},"email":{"type":"number"},"other":{"type":"number","description":"Everything that is not one of the named types — egress IPs, secrets packs, and anything priced since."},"inProgress":{"description":"True while the month is still accruing: the amount is the open billing period's running total, not a settled invoice.","type":"boolean"}},"required":["month","servers","domains","apps","objectStorage","containerRegistry","databases","redis","email","other"],"description":"One month of spend, broken down by resource type. Amounts are what the project is charged, in major units of its billing currency."},"ProjectStats":{"type":"object","properties":{"resourceHistory":{"type":"array","items":{"$ref":"#/components/schemas/ResourceHistoryMonth"}}},"required":["resourceHistory"],"description":"Spend for the last five closed months plus the month still accruing."},"RedisDatabase":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"planSlug":{"type":"string","description":"Pricing tier the database runs on, e.g. `redis-ram-1gb`."},"planType":{"type":"string","enum":["RAM","FLEX"],"description":"`FLEX` plans extend memory with SSD."},"memoryMb":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"throughputOps":{"nullable":true,"description":"Operations per second the plan allows.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cloudProvider":{"type":"string","description":"The cloud the plan runs on, for colocating with your other resources."},"region":{"type":"string"},"replication":{"type":"boolean","description":"High availability. The dataset is mirrored onto a replica within the plan, so half the plan's memory is reserved for it."},"dataPersistence":{"type":"string","enum":["NONE","AOF_EVERY_WRITE","AOF_EVERY_1_SECOND","SNAPSHOT_EVERY_1_HOUR","SNAPSHOT_EVERY_6_HOURS","SNAPSHOT_EVERY_12_HOURS"]},"status":{"type":"string","enum":["CREATING","ACTIVE","ERROR","TERMINATED"]},"host":{"nullable":true,"description":"Connection host, once the database is `ACTIVE`.","type":"string"},"port":{"nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"}},"required":["id","name","planSlug","planType","memoryMb","throughputOps","cloudProvider","region","replication","dataPersistence","status","host","port","createdAt"],"description":"A managed Redis database."},"RedisDatabaseDetail":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"planSlug":{"type":"string","description":"Pricing tier the database runs on, e.g. `redis-ram-1gb`."},"planType":{"type":"string","enum":["RAM","FLEX"],"description":"`FLEX` plans extend memory with SSD."},"memoryMb":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"throughputOps":{"nullable":true,"description":"Operations per second the plan allows.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"cloudProvider":{"type":"string","description":"The cloud the plan runs on, for colocating with your other resources."},"region":{"type":"string"},"replication":{"type":"boolean","description":"High availability. The dataset is mirrored onto a replica within the plan, so half the plan's memory is reserved for it."},"dataPersistence":{"type":"string","enum":["NONE","AOF_EVERY_WRITE","AOF_EVERY_1_SECOND","SNAPSHOT_EVERY_1_HOUR","SNAPSHOT_EVERY_6_HOURS","SNAPSHOT_EVERY_12_HOURS"]},"status":{"type":"string","enum":["CREATING","ACTIVE","ERROR","TERMINATED"]},"host":{"nullable":true,"description":"Connection host, once the database is `ACTIVE`.","type":"string"},"port":{"nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"type":"string","format":"date-time"},"password":{"nullable":true,"description":"Password for the default user. `null` until the database finishes provisioning.","type":"string"}},"required":["id","name","planSlug","planType","memoryMb","throughputOps","cloudProvider","region","replication","dataPersistence","status","host","port","createdAt","password"],"description":"A managed Redis database, with the credential needed to connect to it."},"RedisUsage":{"type":"object","properties":{"memoryUsedMb":{"type":"number"},"memoryLimitMb":{"type":"number"},"networkUsedBytes":{"nullable":true,"description":"Network traffic this month. `null` when the platform has not reported it yet.","type":"number"}},"required":["memoryUsedMb","memoryLimitMb","networkUsedBytes"],"description":"Current usage. Zeroes while a database is still provisioning, rather than an error, so a caller can render it either way."},"RedisDeployment":{"type":"object","properties":{"deployed":{"type":"boolean","enum":[true]}},"required":["deployed"],"description":"Acknowledges that provisioning has started. The database appears in the list as `CREATING`; poll it to see when it is `ACTIVE`."},"ProjectSecret":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Uppercase, underscore-separated, e.g. `DB_PASSWORD`."},"description":{"nullable":true,"type":"string"},"environment":{"$ref":"#/components/schemas/ProjectEnvironment"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Incremented every time the value is replaced. Starts at 1."},"createdBy":{"type":"string","description":"Id of the member who created it."},"updatedBy":{"type":"string","description":"Id of the member who last changed it."},"createdByUser":{"$ref":"#/components/schemas/Actor"},"updatedByUser":{"$ref":"#/components/schemas/Actor"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","description","environment","version","createdBy","updatedBy","createdByUser","updatedByUser","createdAt","updatedAt"],"description":"A project secret, without its value."},"RevealedProjectSecret":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"nullable":true,"type":"string"},"environment":{"$ref":"#/components/schemas/ProjectEnvironment"},"version":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"createdAt":{"description":"Present on create.","type":"string","format":"date-time"},"updatedAt":{"description":"Present on update.","type":"string","format":"date-time"},"value":{"type":"string","description":"The plaintext value. Returned only here — store it before discarding the response."},"maskedValue":{"type":"string","description":"The value with its middle replaced, e.g. `su••••et`. Safe to display."}},"required":["id","name","description","environment","version","value","maskedValue"],"description":"A secret together with its plaintext value, returned once at the moment it is set."},"SecretsUsage":{"type":"object","properties":{"used":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Secrets currently stored on the project."},"limit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many the project may store, free allowance plus purchased packs."},"freeLimit":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Secrets included before any pack is bought."},"packSize":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many secrets one pack adds."},"paidPacks":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Packs currently on the subscription."},"packPrice":{"nullable":true,"description":"`null` when pack pricing is unavailable.","type":"object","properties":{"monthly":{"type":"number","description":"Price per pack per month, in minor units of `currency`."},"currency":{"type":"string","description":"ISO 4217 currency code."}},"required":["monthly","currency"]}},"required":["used","limit","freeLimit","packSize","paidPacks","packPrice"],"description":"Secret capacity and what it costs to add more."},"SecretAuditEntry":{"type":"object","properties":{"id":{"type":"string"},"secretId":{"type":"string"},"action":{"type":"string","enum":["CREATED","UPDATED","DELETED"]},"actorId":{"type":"string"},"actor":{"$ref":"#/components/schemas/Actor"},"metadata":{"nullable":true,"type":"string"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","secretId","action","actorId","actor","metadata","createdAt"],"description":"One entry in a secret's audit trail. Values never appear here — only who did what, and when."},"CostPreview":{"type":"object","properties":{"subtotal":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Before tax and credit."},"tax":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"creditApplied":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Project credit put towards this charge."},"dueToday":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"What will actually be charged now."},"monthly":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Recurring charge from the next billing date."},"currency":{"type":"string","description":"ISO 4217 currency code, uppercase."},"nextBillingDate":{"type":"string","format":"date-time"}},"required":["subtotal","tax","creditApplied","dueToday","monthly","currency","nextBillingDate"],"description":"What adding this resource costs, today and each month after."},"ServerSshKey":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"fingerprint":{"type":"string"}},"required":["id","name","fingerprint"],"description":"An SSH key authorised on a server."},"Server":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string","description":"The project the server belongs to."},"name":{"type":"string"},"type":{"type":"string","enum":["LAMP","POSTGRES"],"description":"What the server was provisioned to run."},"region":{"type":"string"},"instanceType":{"type":"string","description":"Size the server runs on."},"status":{"type":"string","enum":["PROVISIONING","RUNNING","STOPPED","ERROR","TERMINATED"],"description":"`PROVISIONING` also covers a power action still settling."},"ipAddress":{"nullable":true,"description":"Public IPv4 address, once one has been assigned.","type":"string"},"hostname":{"nullable":true,"type":"string"},"sshUser":{"nullable":true,"description":"Login user provisioned at first boot. `null` means the server was created with no keys and cannot be logged in to.","type":"string"},"phpVersion":{"nullable":true,"description":"Set on LAMP servers.","type":"string"},"pgVersion":{"nullable":true,"description":"Set on PostgreSQL servers.","type":"string"},"pgDatabase":{"nullable":true,"type":"string"},"pgUsername":{"nullable":true,"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","name","type","region","instanceType","status","ipAddress","hostname","sshUser","phpVersion","pgVersion","pgDatabase","pgUsername","createdAt","updatedAt"],"description":"A dedicated server in a project."},"ServerDetail":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string","description":"The project the server belongs to."},"name":{"type":"string"},"type":{"type":"string","enum":["LAMP","POSTGRES"],"description":"What the server was provisioned to run."},"region":{"type":"string"},"instanceType":{"type":"string","description":"Size the server runs on."},"status":{"type":"string","enum":["PROVISIONING","RUNNING","STOPPED","ERROR","TERMINATED"],"description":"`PROVISIONING` also covers a power action still settling."},"ipAddress":{"nullable":true,"description":"Public IPv4 address, once one has been assigned.","type":"string"},"hostname":{"nullable":true,"type":"string"},"sshUser":{"nullable":true,"description":"Login user provisioned at first boot. `null` means the server was created with no keys and cannot be logged in to.","type":"string"},"phpVersion":{"nullable":true,"description":"Set on LAMP servers.","type":"string"},"pgVersion":{"nullable":true,"description":"Set on PostgreSQL servers.","type":"string"},"pgDatabase":{"nullable":true,"type":"string"},"pgUsername":{"nullable":true,"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"sshKeys":{"type":"array","items":{"$ref":"#/components/schemas/ServerSshKey"},"description":"Keys authorised on this server. Fixed at creation — they are written on first boot and cannot be added afterwards."}},"required":["id","organizationId","name","type","region","instanceType","status","ipAddress","hostname","sshUser","phpVersion","pgVersion","pgDatabase","pgUsername","createdAt","updatedAt","sshKeys"],"description":"A server together with the keys that can log into it."},"ServerDeployment":{"type":"object","properties":{"deployed":{"type":"boolean","enum":[true]}},"required":["deployed"],"description":"Acknowledges that provisioning has started. The server appears in the list as `PROVISIONING`; poll it to see when it is `RUNNING`."},"SshKey":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string","description":"The project the key belongs to."},"name":{"type":"string","description":"Customer-facing label, e.g. \"Patrick's laptop\"."},"publicKey":{"type":"string","description":"The full authorized_keys line: `<type> <base64> [comment]`."},"fingerprint":{"type":"string","description":"OpenSSH SHA256 fingerprint, e.g. `SHA256:…`. Unique within the project."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","organizationId","name","publicKey","fingerprint","createdAt","updatedAt"],"description":"A registered SSH public key."},"GeneratedSshKey":{"type":"object","properties":{"id":{"type":"string"},"organizationId":{"type":"string","description":"The project the key belongs to."},"name":{"type":"string","description":"Customer-facing label, e.g. \"Patrick's laptop\"."},"publicKey":{"type":"string","description":"The full authorized_keys line: `<type> <base64> [comment]`."},"fingerprint":{"type":"string","description":"OpenSSH SHA256 fingerprint, e.g. `SHA256:…`. Unique within the project."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"privateKey":{"type":"string","description":"PEM-encoded private key. Returned exactly once, in this response — it is never stored and never logged. Save it before you discard the response body."}},"required":["id","organizationId","name","publicKey","fingerprint","createdAt","updatedAt","privateKey"],"description":"A keypair generated on the caller's behalf: the registered key plus its private half."},"SshKeyDeletion":{"type":"object","properties":{"stillAuthorisedOn":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of existing servers the key remains authorised on."}},"required":["stillAuthorisedOn"],"description":"What deleting a key changed. Removing a key stops it authorising future servers; servers already provisioned with it wrote it to disk at first boot and are not reached back into."},"ProjectVariable":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","description":"Uppercase, underscore-separated, e.g. `LOG_LEVEL`."},"description":{"nullable":true,"type":"string"},"value":{"type":"string"},"environment":{"$ref":"#/components/schemas/ProjectEnvironment"},"createdBy":{"type":"string","description":"Id of the member who created it."},"updatedBy":{"type":"string","description":"Id of the member who last changed it."},"createdByUser":{"$ref":"#/components/schemas/Actor"},"updatedByUser":{"$ref":"#/components/schemas/Actor"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","description","value","environment","createdBy","updatedBy","createdByUser","updatedByUser","createdAt","updatedAt"],"description":"A project variable: non-sensitive configuration held in plaintext. Unlike a secret, the value is returned in full on every read — that is the difference between the two resources, not an oversight."},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"nullable":true,"type":"string"},"url":{"type":"string","format":"uri","description":"Where events are POSTed."},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"description":"The events this endpoint receives."},"enabled":{"type":"boolean"},"secretHint":{"type":"string","description":"The last four characters of the signing secret, for telling two secrets apart. The secret itself is returned only when it is created or rotated."},"consecutiveFailures":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Failed deliveries in a row. Any success resets it to zero."},"disabledAt":{"nullable":true,"type":"string","format":"date-time"},"disabledReason":{"nullable":true,"description":"Why the endpoint is off: switched off by hand, or auto-paused after repeated failures.","type":"string","enum":["MANUAL","CONSECUTIVE_FAILURES"]},"lastSuccessAt":{"nullable":true,"type":"string","format":"date-time"},"lastFailureAt":{"nullable":true,"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","description","url","events","enabled","secretHint","consecutiveFailures","disabledAt","disabledReason","lastSuccessAt","lastFailureAt","createdAt","updatedAt"],"description":"A subscription to platform events."},"CreatedWebhookEndpoint":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"nullable":true,"type":"string"},"url":{"type":"string","format":"uri","description":"Where events are POSTed."},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventType"},"description":"The events this endpoint receives."},"enabled":{"type":"boolean"},"secretHint":{"type":"string","description":"The last four characters of the signing secret, for telling two secrets apart. The secret itself is returned only when it is created or rotated."},"consecutiveFailures":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Failed deliveries in a row. Any success resets it to zero."},"disabledAt":{"nullable":true,"type":"string","format":"date-time"},"disabledReason":{"nullable":true,"description":"Why the endpoint is off: switched off by hand, or auto-paused after repeated failures.","type":"string","enum":["MANUAL","CONSECUTIVE_FAILURES"]},"lastSuccessAt":{"nullable":true,"type":"string","format":"date-time"},"lastFailureAt":{"nullable":true,"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"secret":{"type":"string","description":"The signing secret, in full. Returned only here — store it before discarding the response."}},"required":["id","name","description","url","events","enabled","secretHint","consecutiveFailures","disabledAt","disabledReason","lastSuccessAt","lastFailureAt","createdAt","updatedAt","secret"],"description":"A newly created endpoint, together with its signing secret."},"RotatedWebhookSecret":{"type":"object","properties":{"id":{"type":"string"},"secret":{"type":"string","description":"The new signing secret. The previous one stops verifying immediately."}},"required":["id","secret"],"description":"The result of rotating an endpoint's signing secret."},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string"},"eventType":{"$ref":"#/components/schemas/WebhookEventType"},"eventId":{"type":"string","description":"Identifies the originating event; stable across replays."},"payload":{"type":"object","additionalProperties":{},"description":"The JSON body that was POSTed."},"status":{"type":"string","enum":["PENDING","SUCCEEDED","FAILED"]},"attempt":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Which attempt this row is on."},"nextAttemptAt":{"nullable":true,"description":"When the next attempt is due. `null` unless the delivery is `PENDING`.","type":"string","format":"date-time"},"responseStatus":{"nullable":true,"description":"HTTP status the endpoint answered with.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"responseBody":{"nullable":true,"description":"The endpoint's response body, truncated.","type":"string"},"errorMessage":{"nullable":true,"description":"Why the attempt failed, when it failed before a response.","type":"string"},"durationMs":{"nullable":true,"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"deliveredAt":{"nullable":true,"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","eventType","eventId","payload","status","attempt","nextAttemptAt","responseStatus","responseBody","errorMessage","durationMs","deliveredAt","createdAt"],"description":"One attempt to deliver one event, with whatever the endpoint answered."},"WebhookEndpointStats":{"type":"object","properties":{"succeeded":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"failed":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"pending":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["succeeded","failed","pending"],"description":"Delivery counts per status for one endpoint."},"WebhookEndpointDetail":{"type":"object","properties":{"endpoint":{"$ref":"#/components/schemas/WebhookEndpoint"},"stats":{"$ref":"#/components/schemas/WebhookEndpointStats"}},"required":["endpoint","stats"],"description":"An endpoint with the delivery counts behind its health indicator."},"WebhookDeliveryPage":{"type":"object","properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"nextCursor":{"nullable":true,"description":"Pass as `cursor` to fetch the next page. `null` on the last page.","type":"string"}},"required":["deliveries","nextCursor"],"description":"A page of deliveries, newest first."},"WebhookTestResult":{"type":"object","properties":{"outcome":{"type":"string","enum":["PENDING","SUCCEEDED","FAILED"],"description":"What the single inline attempt produced."},"delivery":{"$ref":"#/components/schemas/WebhookDelivery"}},"required":["outcome","delivery"],"description":"The result of firing a synthetic event at an endpoint."},"ListProjects200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}},"required":["success","data"]},"GetProject200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Project"}},"required":["success","data"]},"UpdateProject200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ProjectSettings"}},"required":["success","data"]},"DeleteProject200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"enum":[null]}},"required":["success","data"]},"UpdateProjectBillingEmail200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ProjectBillingEmail"}},"required":["success","data"]},"ListRedisDatabases200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/RedisDatabase"}}},"required":["success","data"]},"CreateRedisDatabase200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RedisDeployment"}},"required":["success","data"]},"GetRedisDatabase200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RedisDatabaseDetail"}},"required":["success","data"]},"DeleteRedisDatabase200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]},"GetRedisUsage200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RedisUsage"}},"required":["success","data"]},"PreviewRedisCost200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/CostPreview"}},"required":["success","data"]},"ListSecrets200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProjectSecret"}}},"required":["success","data"]},"CreateSecret201Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RevealedProjectSecret"}},"required":["success","data"]},"GetSecret200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ProjectSecret"}},"required":["success","data"]},"UpdateSecret200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RevealedProjectSecret"}},"required":["success","data"]},"GetSecretAudit200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/SecretAuditEntry"}}},"required":["success","data"]},"UpgradeSecrets200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SecretsUsage"}},"required":["success","data"]},"GetSecretsUsage200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SecretsUsage"}},"required":["success","data"]},"ListServers200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Server"}}},"required":["success","data"]},"CreateServer200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ServerDeployment"}},"required":["success","data"]},"GetServer200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ServerDetail"}},"required":["success","data"]},"UpdateServer200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Server"}},"required":["success","data"]},"DeleteServer200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]},"RunServerAction200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/Server"}},"required":["success","data"]},"PreviewServerCost200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/CostPreview"}},"required":["success","data"]},"ListSshKeys200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/SshKey"}}},"required":["success","data"]},"CreateSshKey201Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SshKey"}},"required":["success","data"]},"DeleteSshKey200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/SshKeyDeletion"}},"required":["success","data"]},"GenerateSshKey201Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/GeneratedSshKey"}},"required":["success","data"]},"GetProjectStats200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ProjectStats"}},"required":["success","data"]},"ListVariables200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProjectVariable"}}},"required":["success","data"]},"CreateVariable201Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ProjectVariable"}},"required":["success","data"]},"GetVariable200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ProjectVariable"}},"required":["success","data"]},"UpdateVariable200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/ProjectVariable"}},"required":["success","data"]},"ListWebhookEndpoints200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"required":["success","data"]},"CreateWebhookEndpoint201Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/CreatedWebhookEndpoint"}},"required":["success","data"]},"GetWebhookEndpoint200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookEndpointDetail"}},"required":["success","data"]},"UpdateWebhookEndpoint200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookEndpoint"}},"required":["success","data"]},"DeleteWebhookEndpoint200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","nullable":true,"enum":[null]}},"required":["success","data"]},"ListWebhookDeliveries200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookDeliveryPage"}},"required":["success","data"]},"ReplayWebhookDelivery201Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookDelivery"}},"required":["success","data"]},"ResumeWebhookEndpoint200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookEndpoint"}},"required":["success","data"]},"RotateWebhookSecret200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/RotatedWebhookSecret"}},"required":["success","data"]},"TestWebhookEndpoint200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/WebhookTestResult"}},"required":["success","data"]},"CreateProject200Response":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"$ref":"#/components/schemas/CreatedProject"}},"required":["success","data"]}}}}