# Register a Server Credential

Register the public key of a key pair your server holds. Its tokens are accepted from the moment this answers.

## Overview

- `publicKey`: a P-256 public key as a JWK, `{ "kty": "EC", "crv": "P-256", "x": "…", "y": "…" }` — `x` and `y` 32 bytes each in unpadded base64url, a point on the curve. `alg` (`ES256`), `use` (`sig`), `kid`, `key_ops` and `ext`, which key generators add, are accepted and dropped. Never the private key: a JWK with `d` is refused, and that key pair should not be used. How to make one: [Server Credentials](https://docs.aureahub.com/docs/guide-server-credentials.md).
- `name`: your label, 1 to 100 characters. `role`: one of `tenantadmin`, `non_signing_admin`, `signer`, `approver`, `editor`, `viewer`, `auditor`, `security_admin`, `security_auditor` — only a role whose permissions are all yours (a `security_admin` does not issue a `signer`). The role decides which routes the credential's tokens reach: it is registered with `roleNarrows` `true` ([What a Credential's Role Reaches](https://docs.aureahub.com/docs/guide-server-credentials.md)).
- `allowedIps` (optional): up to 50 IPv4 or IPv6 networks or addresses; empty or left out: any address. `expiresAt` (optional): in the future, at most 5 years ahead; `null` or left out: no expiry.
- `201` with the credential: its `id` is the `kid` of its tokens. At most 10 active credentials per tenant.
- For the tenant roles `owner`, `tenantadmin`, `non_signing_admin` and `security_admin`, on their own tenant.
- `testOnly` (optional) [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md): `true` registers a credential for the test networks only ([Server Credentials › Test-Only Credentials](https://docs.aureahub.com/docs/guide-server-credentials.md)); the answer then carries `"testOnly": true`. Left out or `false`: a credential like every other, answered without the field.

> Note: **Your tenant's quorum** [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md) — where it holds administrators' actions (a tenant on the institutional security profile by its profile; every other tenant's records them only): this call answers `202` `{ "state": "awaiting_quorum", "requestId" }` — the **202** tab below — and nothing is done until another administrator of your tenant approves it; the answer it would have given is then read once by who asked ([Read the Answer](https://docs.aureahub.com/docs/quorum-request-result.md)). With no other administrator able to approve it, it is done at once. [Your Tenant's Quorum](https://docs.aureahub.com/docs/guide-tenant-quorum.md). Every other tenant gets the answers below, unchanged.

## Endpoint

### `POST /v1/admin/tenants/{id}/server-credentials`

Authentication: bearer token required.

Registers a P-256 public key as a server credential of the tenant.

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Tenant UUID |

**Request body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | 1–100 characters |
| `role` | string | yes | A tenant role within yours |
| `publicKey` | object | yes | The public key as a JWK: `kty`, `crv`, `x`, `y` |
| `allowedIps` | string[] | no | Networks or addresses its tokens are accepted from |
| `expiresAt` | string (date-time) \| null | no | When its tokens stop being accepted |
| `testOnly` | boolean | no | `true`: its tokens reach the test networks only (switched on per tenant) |

**Responses**

`201` Registered

```json
{
  "id": "2f6d8a4e-1c3b-4e5f-9a7d-0b2c4e6f8a1c",
  "tenantId": "6f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0",
  "name": "Payments server",
  "role": "signer",
  "status": "active",
  "publicKey": {
    "kty": "EC",
    "crv": "P-256",
    "x": "kTxt6XgA4C3r8RE3iDiksXglx-XqsKZBKcJXz8xb9Bg",
    "y": "2UOzdwbCN0bU_vBmtae1dx458v3t1PXqowMQIp9qahU"
  },
  "thumbprint": "eh1D5t_soPjcyGjYMXEeNtyTxia1As4QO91SxspJPfM",
  "allowedIps": ["203.0.113.0/24"],
  "expiresAt": "2027-10-06T00:00:00.000Z",
  "createdAt": "2026-10-06T08:40:12.307Z",
  "createdBy": "5d2c1b0a-9e8f-4a7b-8c6d-5e4f3a2b1c0d",
  "updatedAt": "2026-10-06T08:40:12.307Z",
  "updatedBy": "5d2c1b0a-9e8f-4a7b-8c6d-5e4f3a2b1c0d",
  "lastUsedAt": null,
  "revokedAt": null,
  "revokedBy": null,
  "revokeReason": null,
  "roleNarrows": true
}
```

`202` Awaiting quorum

```json
{
  "state": "awaiting_quorum",
  "requestId": "4e2b9c17-8a3d-4f60-b5e1-7c9d0a2f6b38"
}
```

`400` Private key

```json
{
  "statusCode": 400,
  "error": "BadRequestError",
  "message": "publicKey carries a private key (\"d\"): send the public key only, and do not use this key pair",
  "details": { "code": "SERVER_CREDENTIAL_PRIVATE_KEY" }
}
```

`409` Key in use

```json
{
  "statusCode": 409,
  "error": "ConflictError",
  "message": "This public key is already registered: generate a new key pair",
  "details": { "code": "SERVER_CREDENTIAL_KEY_IN_USE" }
}
```

## Refusals

Nothing is registered. A body that does not fit the schema — a missing field, a wrong type, a name of spaces only — answers `400` `Request validation failed`; the rest carry `details.code`:

- `400` `SERVER_CREDENTIAL_KEY_INVALID` — `publicKey must be a P-256 public key as a JWK:` and why, for example `crv must be "P-256"`, `x must be 32 bytes in unpadded base64url` or `the point is not on the curve`.
- `400` `SERVER_CREDENTIAL_PRIVATE_KEY` — the JWK carries `d`.
- `400` `SERVER_CREDENTIAL_ROLE_INVALID` — `role must be one of tenantadmin, non_signing_admin, signer, approver, editor, viewer, auditor, security_admin, security_auditor`. `SERVER_CREDENTIAL_NAME_INVALID` — `name must hold 1 to 100 characters`.
- `400` `SERVER_CREDENTIAL_IP_INVALID`, with `details.index` — `allowedIps[<index>]` and why, for an entry that is not an address or network, has host bits under its mask, or is IPv4-mapped IPv6.
- `400` `SERVER_CREDENTIAL_EXPIRY_INVALID` — `expiresAt must be a time in the future, at most 5 years ahead (or null: no expiry)`.
- `403` `SERVER_CREDENTIAL_ROLE_ABOVE_OWN` — `A credential's role holds permissions your role does not`.
- `403` `TEST_ONLY_CREDENTIALS_OFF` — `Test-only server credentials are not switched on for this tenant. Register the credential without testOnly, or ask help@aureahub.com to switch them on.`
- `404` `TENANT_NOT_FOUND` — `Tenant not found`.
- `409` `SERVER_CREDENTIAL_KEY_IN_USE` — the key was registered before, here or anywhere, revoked or not. `SERVER_CREDENTIAL_LIMIT` — `A tenant holds at most 10 active server credentials: revoke one first`.

---

Web version: https://docs.aureahub.com/#server-credential-create
