# certificates (/docs/reference/api/certificates)

> TLS certificates on a provider account (DigitalOcean first), for the tenant's [load balancers](/docs/reference/api/load-balancers). Every call records what the tenant wants and returns at once; the `vps.provider-resources` reconciler converges the provider through `ProviderImpl.certificates`. Reading takes `compute:certificates:read`, everything else `compute:certificates:manage`, on `provider-certificate/{id}`. Certificates book no quota (at most 100 per tenant) and do not bill.



TLS certificates on a provider account (DigitalOcean first), for the tenant's [load balancers](/docs/reference/api/load-balancers). Every call records what the tenant wants and returns at once; the `vps.provider-resources` reconciler converges the provider through `ProviderImpl.certificates`. Reading takes `compute:certificates:read`, everything else `compute:certificates:manage`, on `provider-certificate/{id}`. Certificates book no quota (at most 100 per tenant) and do not bill.

All 4 methods are served by @better-compute/server 0.3.0.

| Method              | Kind     | Authorization                                                  | Phase | Status     |
| ------------------- | -------- | -------------------------------------------------------------- | ----- | ---------- |
| [`create`](#create) | mutation | `compute:certificates:manage` @ `compute-provider-certificate` | v1.0  | registered |
| [`list`](#list)     | read     | `compute:certificates:read` @ `compute-tenant` + items         | v1.0  | registered |
| [`get`](#get)       | read     | `compute:certificates:read` @ `compute-provider-certificate`   | v1.0  | registered |
| [`delete`](#delete) | mutation | `compute:certificates:manage` @ `compute-provider-certificate` | v1.0  | registered |

## create [#create]

`POST /api/compute/certificates/create`

Makes a certificate and returns it `pending`. `kind: 'managed'` has the provider issue and renew it for `domains`, each of which must be in one of the tenant's own [DNS zones](/docs/reference/api/dns-zones) on the same provider account (`INVALID_INPUT`, reason `domainNotOwned`): the provider issues certificates for any domain the account hosts, which may be another tenant's. `kind: 'custom'` uploads the tenant's own PEMs: `certificate`, `privateKey` and optionally `chain` are references to the tenant's own secrets, `secret://<tenantId>/<id>`, and any other scheme or tenant is `INVALID_INPUT`. The row keeps only the references; they are resolved in the call that uploads the PEMs, by a resolver that refuses every reference the certificate does not name, and never appear in a view, event or log. The name is unique in the tenant (`CONFLICT`); the same `idempotencyKey` returns the same certificate. Publishes `providerCertificate.created`, then `providerCertificate.ready` or `providerCertificate.error`.

* **Kind:** mutation
* **Authorization:** `compute:certificates:manage` @ `compute-provider-certificate`
* **Module:** `vps`
* **Phase:** v1.0 (registered)

**Input**

| Field            | Type                       | Required | Constraints                                                                                    |
| ---------------- | -------------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `tenantId`       | string                     | yes      | at most 128 characters; matches `^[A-Za-z0-9._:-]{1,128}$`                                     |
| `projectId`      | string                     |          | at most 56 characters; matches `^prj_[0-9a-hjkmnp-tv-z]{26,52}$`                               |
| `poolId`         | string                     | yes      | at most 57 characters; matches `^pool_[0-9a-hjkmnp-tv-z]{26,52}$`                              |
| `name`           | string                     | yes      | at most 42 characters; matches `^[a-z]([-a-z0-9]{0,40}[a-z0-9])?$`                             |
| `kind`           | one of `managed`, `custom` | yes      |                                                                                                |
| `domains`        | list of string             |          | 1-100 items                                                                                    |
| `certificate`    | string                     |          | at most 270 characters; matches `^secret:\/\/([A-Za-z0-9._:-]{1,128})\/[A-Za-z0-9._-]{1,128}$` |
| `privateKey`     | string                     |          | at most 270 characters; matches `^secret:\/\/([A-Za-z0-9._:-]{1,128})\/[A-Za-z0-9._-]{1,128}$` |
| `chain`          | string                     |          | at most 270 characters; matches `^secret:\/\/([A-Za-z0-9._:-]{1,128})\/[A-Za-z0-9._-]{1,128}$` |
| `idempotencyKey` | string                     | yes      | at most 128 characters; matches `^[A-Za-z0-9._:-]{1,128}$`                                     |

## list [#list]

`POST /api/compute/certificates/list`

Returns the tenant's certificates in the order they were made (at most 1000), optionally of one pool or project. Deleted ones only with `includeDeleted`.

* **Kind:** read
* **Authorization:** `compute:certificates:read` @ `compute-tenant` + items
* **Module:** `vps`
* **Phase:** v1.0 (registered)

**Input**

| Field            | Type    | Required | Constraints                                                       |
| ---------------- | ------- | -------- | ----------------------------------------------------------------- |
| `tenantId`       | string  | yes      | at most 128 characters; matches `^[A-Za-z0-9._:-]{1,128}$`        |
| `poolId`         | string  |          | at most 57 characters; matches `^pool_[0-9a-hjkmnp-tv-z]{26,52}$` |
| `projectId`      | string  |          | at most 56 characters; matches `^prj_[0-9a-hjkmnp-tv-z]{26,52}$`  |
| `includeDeleted` | boolean |          | default `false`                                                   |

## get [#get]

`POST /api/compute/certificates/get`

Returns one certificate: its kind, names, state and, once issued or uploaded, `notAfter` and `fingerprint`.

* **Kind:** read
* **Authorization:** `compute:certificates:read` @ `compute-provider-certificate`
* **Module:** `vps`
* **Phase:** v1.0 (registered)

**Input**

| Field           | Type   | Required | Constraints                                                        |
| --------------- | ------ | -------- | ------------------------------------------------------------------ |
| `tenantId`      | string | yes      | at most 128 characters; matches `^[A-Za-z0-9._:-]{1,128}$`         |
| `certificateId` | string | yes      | at most 58 characters; matches `^pcert_[0-9a-hjkmnp-tv-z]{26,52}$` |

## delete [#delete]

`POST /api/compute/certificates/delete`

Marks the certificate for deletion and returns it `deleting`; the reconciler deletes it at the provider and publishes `providerCertificate.deleted`. A certificate a load balancer of the tenant still names is `INVALID_STATE` (reason `inUse`): change or delete that load balancer first. A certificate is immutable; to change one, make another and point the load balancer at it.

* **Kind:** mutation
* **Authorization:** `compute:certificates:manage` @ `compute-provider-certificate`
* **Module:** `vps`
* **Phase:** v1.0 (registered)

**Input**

| Field           | Type   | Required | Constraints                                                        |
| --------------- | ------ | -------- | ------------------------------------------------------------------ |
| `tenantId`      | string | yes      | at most 128 characters; matches `^[A-Za-z0-9._:-]{1,128}$`         |
| `certificateId` | string | yes      | at most 58 characters; matches `^pcert_[0-9a-hjkmnp-tv-z]{26,52}$` |
