Skip to main content
POST
Create a database cluster

Authorizations

Authorization
string
header
required

BASALTIC-HMAC-SHA256 request signing for service-account (access-key) auth. An AWS-SigV4-style scheme: an HMAC-SHA256 over a canonical request, keyed by a signing key derived from your secret access key. The basaltic CLI does this for you; the steps below are for a non-CLI client.

Headers you send

  • Authorization (this scheme) — format below.
  • X-Date: request time in UTC, compact ISO-8601 basic: YYYYMMDDTHHMMSSZ (e.g. 20240115T103000Z). Must be within 5 minutes of server time.
  • X-Nonce: random per-request value (e.g. 16+ bytes of hex). Required — it is what makes each signature unique so the server can reject replays. X-Date alone is not enough (one-second resolution; UNSIGNED-PAYLOAD never enters the canonical request).
  • X-Content-Sha256 (optional): lowercase-hex SHA-256 of the request body, to bind the exact bytes to the signature — the server re-hashes and rejects a mismatch. Omit it, and put the literal UNSIGNED-PAYLOAD in the body-hash slot, for an empty body or a streaming upload that shouldn't be buffered.

Authorization header format

  • <date>: YYYYMMDD (the day part of X-Date).
  • <region>: target region code (e.g. sa-saopaulo-1), or global for the region-agnostic (IAM) endpoints.
  • <service>: basaltic.
  • SignedHeaders: the covered header names, lowercased and sorted, joined by ; — at minimum host;x-date;x-nonce (all three required).
  • Signature: lowercase hex from step 4.
  • Note the , (comma + space) between the three Authorization parameters.

Step 1 — canonical request

Join these six lines with \n (LF):

  • canonical-path: the URL-escaped request path (e.g. /v1/compute/instances).
  • canonical-query: each query key and value URL-encoded, sorted by key then value, joined by &; empty string when there is no query.
  • canonical-headers: for each signed header, name:value (name lowercased, value trimmed), sorted by name, each followed by a trailing \n.
  • signed-headers: the same names, lowercased, sorted, joined by ;.
  • body-hash: lowercase-hex SHA-256 of the body, or the literal UNSIGNED-PAYLOAD.

Step 2 — string to sign

Join with \n:

The second line is the credential date at midnight — NOT the X-Date value — and the third is the credential scope without the access-key id.

Step 3 — derive the signing key (HMAC chain over your secret)

Step 4 — signature

Example (POST with a JSON body)

Request validity

  • Valid for 5 minutes from the X-Date timestamp.
  • The signature must cover at least host, x-date, and x-nonce.
  • Mutating requests (POST/PUT/PATCH/DELETE) that present a signed nonce are anti-replay guarded for the drift window; omitting X-Nonce is rejected.

Rate limits

There is no global request budget: a limit applies only where an operation documents a 429 response, and each such endpoint counts its own fixed window. Everything else is bounded by quota, not by request rate.

Per-endpoint request limits

A few endpoints are limited in front of the handler, counted per caller — the authenticated principal when the request carries credentials, the client IP otherwise. On this API surface that is the public region catalogue, GET /v1/regions: it guards no secret, and the budget is there because the catalogue is unauthenticated and database-backed.

An operation that is limited says so by documenting a 429. These responses — 429 and 2xx — carry the budget, so a client can pace itself instead of discovering the ceiling by hitting it:

  • X-RateLimit-Limit — requests allowed per window on this endpoint. Authoritative: read it rather than hard-coding a number.
  • X-RateLimit-Remaining — requests left in the window (0 on a 429).
  • X-RateLimit-Reset — seconds until the window resets. A duration, not a timestamp, so it needs no clock agreement.
  • Retry-After — on a 429 only; seconds to wait. Never zero.

Over the limit the response is 429 with error code RATE_LIMITED. Honor Retry-After — retrying sooner is refused and extends the window, since the refused attempt is itself counted.

Service-level limits

Some resources meter the work itself rather than the request, and are documented on the operation. They return 429 with their own error code and no rate-limit headers, because the budget is not a per-endpoint window: sending email is capped per (account, identity) per second (EMAIL_RATE_LIMIT), and charge attempts are capped per invoice (INVOICE_PAYMENT_ATTEMPTS_EXHAUSTED).

Headers

Idempotency-Key
string

Optional client-generated key that makes a create replay-safe. Retrying a request with the same key returns the original outcome verbatim instead of creating a duplicate resource. Reusing a key with a different request body is rejected (422); a request whose key is still being processed returns 409. Records are honored for 24 hours. Use a UUID or similarly unique token.

Maximum string length: 255

Body

application/json
name
string
required
Example:

"prod-orders-db"

engine_type
enum<string>
required
Available options:
postgres,
valkey
Example:

"postgres"

flavor_id
string<uuid>
required

Compute flavor each cluster member runs on. Must be a database-family flavor.

Example:

"e5f6a7b8-c9d0-4123-e4f5-a6b7c8d9e0f1"

storage_gb
integer
required
Required range: x >= 1
Example:

100

networks
object[]
required

At least one network interface is required.

Minimum array length: 1
description
string
Example:

"Primary orders database"

engine_version
string

e.g. '17' for postgres. Defaults to the engine's current default when omitted.

Example:

"17"

instance_count
integer
default:1

Node count. Postgres: 1 = single-node; 2+ adds Patroni HA members (max 10). Valkey: 1 = single-node; 3 = primary + replicas with Sentinel (2 is rejected; max 3).

Required range: x >= 1
Example:

1

admin_user
string

Optional; defaults to "admin". Postgres-safe identifier only.

Pattern: ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
Example:

"admin"

default_database
string

Optional; defaults to "default". Postgres-safe identifier only.

Pattern: ^[a-zA-Z_][a-zA-Z0-9_]{0,63}$
Example:

"default"

key_names
string[]

Platform-operator break-glass only. Stamps SSH keypairs onto the cluster's managed VMs, which run the platform's own software; a tenant reaches its database over the cluster endpoint, never over SSH. Accepted only from the platform account and only with database:StampBreakGlassKeys — any other account setting it is rejected with 400 INVALID_INPUT.

parameter_group_id
string<uuid>

Binds the cluster to a parameter group whose settings its members boot with and converge to. Omitted means engine defaults. The group must match the cluster's engine type and major version.

Example:

"1a2b3c4d-5e6f-4071-8293-a4b5c6d7e8f9"

assign_public_ip
boolean
default:true

Endpoint exposure. True (the default) allocates a floating IP per endpoint, and requires the cluster's subnet to carry a default route (0.0.0.0/0) to an internet gateway — a public cluster on a subnet without one is rejected before anything is provisioned. False allocates no floating IP: endpoints resolve to member addresses inside the VPC, which is how a cluster is placed on a private subnet. IPv6 reachability is unaffected either way — it follows the subnet's ::/0 route, as it does for every other resource.

Example:

true

metadata
object
Example:
tags
object
Example:
restore_from
object

Bootstrap the new cluster from an existing backup instead of a fresh initdb. The source backup must belong to the same account and be in status=succeeded. Single-node only in the current pass.

Response

Accepted — cluster provisioning started

cluster
object