# Manage API keys

> Generate, rotate and revoke the x3d_live_ key that authenticates the print API — from the browser, because these endpoints use your session rather than a key.

Source: https://x3dstudios.com/docs/api/keys  ·  Updated: 2026-09-09

One account holds one API key. This page covers the three calls behind the Developer API key panel on /profile: read the key's status, generate or rotate it, and revoke it. All three are authenticated by your signed-in browser session — an API key cannot manage API keys.

> **Session-authenticated, not key-authenticated** — These endpoints read the NextAuth session cookie. Sending Authorization: Bearer x3d_live_… to them returns 401. Key management happens in a browser, on purpose: a compromised key must not be able to mint its own replacement.

## Read the key status

`GET /api/account/api-key`

Whether the account has a key, and what it looks like.

Auth: session

**200**

```json
{
  "hasKey": true,
  "prefix": "x3d_live_ab12cd…",
  "createdAt": "2026-09-09T12:00:00.000Z"
}
```

**Response fields**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `hasKey` | boolean | no | true when a key exists on the account. |
| `prefix` | string \| null | no | The non-secret display prefix: x3d_live_ plus the first six characters of the secret and an ellipsis. Enough to recognise which key is deployed where, not enough to use. null when there is no key. |
| `createdAt` | string \| null | no | ISO 8601 timestamp of when the current key was issued. null when there is no key. |

> **The secret is never in this response** — There is no endpoint that returns an existing key. GET tells you a key exists and shows its prefix; that is all we are able to tell you, because the key itself is not stored.

## Generate or rotate

`POST /api/account/api-key`

Issue a new key, replacing any existing one. No request body.

Auth: session

**From the profile page**

```javascript
const res = await fetch("/api/account/api-key", {
  method: "POST",
  credentials: "include",
});

const data = await res.json();
if (res.status === 402) throw new Error(data.error); // code: "CARD_REQUIRED"

// The only time you will ever see this value.
console.log(data.apiKey);
```

**200**

```json
{
  "apiKey": "x3d_live_ab12cd34ef56...",
  "prefix": "x3d_live_ab12cd…",
  "createdAt": "2026-09-09T12:00:00.000Z"
}
```

**Response fields**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `apiKey` | string | yes | The full secret: x3d_live_ followed by 48 lowercase hex characters, 57 characters in total. Returned exactly once and never recoverable. Copy it into a secret store or environment variable before you close the tab. |
| `prefix` | string | yes | The display prefix for the key just issued. Safe to log and to show in a UI. |
| `createdAt` | string | yes | ISO 8601 timestamp of issue. |

> **Rotation is immediate and silent** — There is no grace period and no second active key. The moment this call returns, the previous key stops authenticating and every deployment still using it starts getting 401. Roll it out everywhere before you rotate, not after.

### The card-on-file prerequisite

A key can only be issued to an account with a saved payment method, because API orders bill automatically. Add one from /profile, then generate. If payments are not configured in a deployment at all, the check is skipped and a key is issued without a card — self-hosted setups will see that behaviour, x3dstudios.com will not.

**402 — no card on file**

```json
{
  "error": "Add a card on file first — API print orders are billed automatically to your saved card.",
  "code": "CARD_REQUIRED"
}
```

> **The card is checked when the key is made, not when it is used** — Remove the card later and the key keeps working. Orders still succeed with 201, but come back paid: false at PENDING_PAYMENT with a payment link and a message that says "No card on file". Nothing prints until they are paid.

## Revoke

`DELETE /api/account/api-key`

Delete the key. No request body.

Auth: session

This clears the stored hash, the prefix and the created date. Every request using that key gets 401 from then on. Orders already submitted are unaffected — they are paid work at the farm, and revoking a key does not cancel them, but you also lose the ability to poll them until you generate a new key.

**200**

```json
{ "ok": true }
```

| Method | Status | Body |
| --- | --- | --- |
| GET | 200 | { hasKey, prefix, createdAt } |
| GET / POST / DELETE | 401 | { "error": "Unauthorized" } |
| POST | 200 | { apiKey, prefix, createdAt } |
| POST | 402 | { error, code: "CARD_REQUIRED" } |
| DELETE | 200 | { "ok": true } |

*Every response these three endpoints return.*

## Handling the key

- Store it as an environment variable or in a secret manager. It authorises real charges to your card.
- Keep it server-side. Anything in browser JavaScript, a mobile binary or a public repository is public.
- Log the prefix, never the key. That is what the prefix is for.
- Revoke immediately if it leaks — a revoked key cannot be used, and generating a new one revokes the old one anyway.
- The prefix is not a password hint: it is nine fixed characters plus six of the secret, and it is safe to paste into a support ticket.

[Manage your key on /profile](https://x3dstudios.com/profile)

- [Authentication](https://x3dstudios.com/docs/api/authentication) — Which headers the API reads and how the key is checked.
- [Submit a print order](https://x3dstudios.com/docs/api/submit-order) — The endpoint the key exists for.
- [Billing](https://x3dstudios.com/docs/api/billing) — How API orders are charged, and what happens when a card fails.

## Frequently asked

**I lost my key. Can I get it back?**

No. We store a SHA-256 hash of it and a display prefix, never the key itself, so there is nothing to show you. Generate a new one — that replaces the old key immediately.

**Why does generating a key need a card on file?**

Orders submitted through the API charge the saved card the moment they succeed. There is no checkout page in between, so a key with no card behind it would only ever produce unpaid orders.

**Can I have more than one key at a time?**

No. An account holds exactly one key. Generating again replaces the previous one, which stops working the instant the new one is issued.
