# Capacity API

Let your application plan its own database boosts, such as before a ticket sale it knows of, with the capacity key of its environment.


Your application often knows when a peak comes: a shop knows when a sale opens. With the **capacity key** of its environment it plans a [boost](/running/database-capacity) of its own database itself. It never gets a Scaleway key: it asks Brewless, and Brewless changes the database.

Boosts come with the Business plan and up.

## The capacity key

An owner or admin switches the key on in the console: **Databases**, the database, **Capacity key of the application**, **Switch on**. The key is shown once, with the three variables the application reads:

```dotenv .env
BREWLESS_API_URL=https://acme.brewless.eu
BREWLESS_ENVIRONMENT=01JD…
BREWLESS_CAPACITY_KEY=bcap_…
```

Put them in the environment's variables (`brewless env:push`, or the console). **New key** replaces the key at once; **Switch off** refuses every request with it from then on.

The key reaches only the Serverless SQL Databases of its own environment, and only the boosts it asked for itself. When you switch it on, you set what a request with it may ask for, at most:

| Limit | Default |
| --- | --- |
| Highest minimum | 15 vCPU |
| Highest maximum | 15 vCPU |
| Hours a boost | 12 |
| Boost hours a month | 100 |
| Days ahead | 365 |

Boost hours count everything a boost holds: warming up, the window and cooling down. A boost over the end of a month counts in each month for its part there.

## With brewless/laravel

The `brewless/laravel` package has a `CapacityClient` that signs every request for you. A shop that plans a boost when an event with a sale is saved:

```php app/Observers/EventObserver.php
use Brewless\Laravel\Capacity\CapacityApi;
use Brewless\Laravel\Capacity\CapacityException;

public function saved(Event $event): void
{
    try {
        resolve(CapacityApi::class)->schedule(
            "event-{$event->id}-sale",
            $event->sale_starts_at,
            $event->sale_starts_at->addHours(2),
            min: 4,
            max: 15,
            prewarm: 15,
            cooldown: 30,
        );
    } catch (CapacityException $exception) {
        report($exception);
    }
}
```

When the sale moves, send the new times under the same key; when the event is cancelled, cancel the boost. Just before the start, `ready()` says whether the database has its minimum: tell your team when it has not, and never hold the sale back for it.

## When Brewless cannot be reached

A boost is a help, not something your application depends on. Catch the client's exceptions and go on: when Brewless or Scaleway cannot be reached, the database keeps scaling between its normal bounds as always.

## The requests

Every request goes to `{BREWLESS_API_URL}/api/runtime/capacity` and is signed the same way as [databases per tenant](/running/tenant-databases#the-requests): an HMAC-SHA256 with the capacity key over the method, the path, the time and the SHA-256 of the body, each on a line of its own, in three headers (`X-Brewless-Environment`, `X-Brewless-Timestamp`, `X-Brewless-Signature`). A request more than five minutes off is refused.

| Request | What it does |
| --- | --- |
| `GET /databases` | The databases of the environment, with their normal bounds, what they should have now and what Scaleway gives now; the limits of the key and the boost hours used this month |
| `POST /boosts` | Plan a boost, or start one now |
| `GET /boosts/{key}` | A boost: its phase, `ready`, its window and what it costs |
| `PATCH /boosts/{key}` | Change a boost before it starts warming up; after that only a later end |
| `POST /boosts/{key}/extend` | A later end |
| `DELETE /boosts/{key}` | Cancel: before it starts at once, while it runs it ends now and cools down |

A boost asks for:

| Field | What it is |
| --- | --- |
| `key` | Your own name for it, such as `event-123-sale`: letters, digits and `. _ : -`, at most 100 |
| `min_vcpu`, `max_vcpu` | The bounds during the boost |
| `starts_at` | When it starts, with its offset (`2027-05-01T10:00:00+02:00`); left out, it starts now |
| `ends_at` or `minutes` | When it ends, or for how long |
| `prewarm_minutes` | Minutes to warm up before the start; 15 for a planned boost, 0 for one that starts now |
| `cooldown_minutes` | Minutes to cool down after the end; 30 unless given |
| `database` | The name of the database; left out, the environment's own |
| `allow_overlap` | Plan a second boost with the same window and vCPUs under another key |

The `key` makes a request safe to send again: the same key with the same request gives the boost it made before (`200`), the same key with another request is refused (`409`). A boost that was cancelled and is over gives its key free: sent again, the key plans a new boost, for an event that goes ahead after all. A change with `PATCH` is accepted once per signature; a repeat of a boost, a later end or a cancellation may come twice.

`ready` is `true` only once Scaleway gave the database the boost's minimum.

| Answer | When |
| --- | --- |
| `201` | A new boost |
| `200` | The boost as it stands |
| `401` | The signature is wrong, the time is off, or the key is not known |
| `403` | The key is switched off, or the plan has no boosts |
| `404` | No such boost or database for this environment |
| `409` | The key was used for another request |
| `422` | A value or a limit of the key is not kept, with the field it is about |
| `429` | More than 30 requests a minute from the environment |

Every answer has `Cache-Control: no-store`.
