---
title: Create key
description: Issue a program credential with a cluster, namespace, or collection scope.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# Create key

`POST /keys`

Issue a program credential with a cluster, namespace, or collection scope.

**Permission:** Cluster key, or a namespace key managing collection keys within its namespace.

[API key types and authentication](/engine/reference/authentication.md).

The secret appears only in the create response. Store it securely before continuing.

Namespace and collection scopes require a nonempty actions list. New actions must be a subset of the creator’s actions.

Keys remain active until revoked. They have no expiry field.

## Example request

```http
POST /keys HTTP/1.1
Host: engine.example.com
Authorization: Bearer <GRAPHON_ENGINE_API_KEY>
Content-Type: application/json

{
  "name": "acme-read",
  "scope": {
    "kind": "namespace",
    "namespace": "acme",
    "actions": [
      "read",
      "search",
      "query"
    ]
  }
}
```

## Request body

### `name`

**string · required**

Name that identifies this credential for administration. Use the resource-name grammar: 1–63 lowercase letters, digits, underscores, or hyphens.

### `scope`

**object · required**

Authority granted to the credential. Choose a cluster, namespace, or collection scope.

#### cluster

##### `scope.kind`

**"cluster" · required**

Cluster-wide authority. A cluster key can manage all namespaces and keys, and perform every data action.

#### namespace

##### `scope.actions`

**array<string> · required**

Nonempty list of permitted actions. write includes read. search and query are independent and do not include read. A new key cannot exceed its creator’s actions.

- `read`: Read resources and job state.
- `write`: Create, change, and delete resources. Includes read.
- `search`: Retrieve ranked evidence from allowed collections.
- `query`: Generate cited answers from allowed collections.

##### `scope.kind`

**"namespace" · required**

Namespace scope limits this key to one tenant boundary.

##### `scope.namespace`

**string · required**

Name of the namespace this key can access. It cannot see resources in other namespaces.

#### collection

##### `scope.actions`

**array<string> · required**

Nonempty list of permitted actions. write includes read. search and query are independent and do not include read. A new key cannot exceed its creator’s actions.

- `read`: Read resources and job state.
- `write`: Create, change, and delete resources. Includes read.
- `search`: Retrieve ranked evidence from allowed collections.
- `query`: Generate cited answers from allowed collections.

##### `scope.collections`

**array<string> · required**

Nonempty list of collection names within the named namespace. The key cannot create collections or issue other keys.

- Minimum items: 1

##### `scope.kind`

**"collection" · required**

Collection scope limits this key to the named collections in one namespace.

##### `scope.namespace`

**string · required**

Namespace name that contains all collections in this scope.

## Responses

### 201 · Key created

The new credential. secret appears only in this response.

Content type: `application/json`.

#### Response headers

##### `x-request-id`

**string · required**

Request correlation ID. Retain it when investigating a failed or unexpected request.

##### `Location`

**string · required**

The path of the new resource, by ids.

```json
{
  "id": "key_01K6Q6N7D8E9F0G1H2J3K4M5N6",
  "name": "acme-read",
  "prefix": "gek_",
  "scope": {
    "kind": "namespace",
    "namespace": "acme",
    "actions": [
      "read",
      "search",
      "query"
    ]
  },
  "created_at": "2026-10-06T10:00:00Z",
  "secret": "<GRAPHON_ENGINE_API_KEY>"
}
```

#### Response fields

##### `created_at`

**string (date-time) · required**

Time the resource was created, as an ISO 8601 UTC timestamp.

##### `id`

**string · required**

Immutable administrative key ID with the key_ prefix. This is not the secret used to authenticate.

##### `name`

**string · required**

Administrative name assigned when this key was created.

##### `prefix`

**string · required**

Short non-secret prefix used to identify the credential.

##### `revoked_at`

**string (date-time) · optional · nullable**

Time the key was revoked. Omitted while the key remains active.

##### `scope`

**object · required**

Resource scope and actions granted to this key.

###### cluster

###### `scope.kind`

**"cluster" · required**

Cluster-wide authority. A cluster key can manage all namespaces and keys, and perform every data action.

###### namespace

###### `scope.actions`

**array<string> · required**

Nonempty list of permitted actions. write includes read. search and query are independent and do not include read. A new key cannot exceed its creator’s actions.

- `read`: Read resources and job state.
- `write`: Create, change, and delete resources. Includes read.
- `search`: Retrieve ranked evidence from allowed collections.
- `query`: Generate cited answers from allowed collections.

###### `scope.kind`

**"namespace" · required**

Namespace scope limits this key to one tenant boundary.

###### `scope.namespace`

**string · required**

Name of the namespace this key can access. It cannot see resources in other namespaces.

###### collection

###### `scope.actions`

**array<string> · required**

Nonempty list of permitted actions. write includes read. search and query are independent and do not include read. A new key cannot exceed its creator’s actions.

- `read`: Read resources and job state.
- `write`: Create, change, and delete resources. Includes read.
- `search`: Retrieve ranked evidence from allowed collections.
- `query`: Generate cited answers from allowed collections.

###### `scope.collections`

**array<string> · required**

Nonempty list of collection names within the named namespace. The key cannot create collections or issue other keys.

- Minimum items: 1

###### `scope.kind`

**"collection" · required**

Collection scope limits this key to the named collections in one namespace.

###### `scope.namespace`

**string · required**

Namespace name that contains all collections in this scope.

##### `secret`

**string · optional · nullable**

Credential returned only once, in the create response. Store it securely on your server. Later list responses omit it.

## Errors

[Error format and retry guidance](/engine/reference/errors.md).

### 400 · `invalid_request`

A field, path, parameter, or conditional request rule is invalid.

Correct the request. Unknown fields are rejected.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "A field, path, parameter, or conditional request rule is invalid.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 401 · `unauthenticated`

The API key is missing, invalid, or revoked.

Send an active key in Authorization: Bearer or X-API-Key.

```json
{
  "error": {
    "code": "unauthenticated",
    "message": "The API key is missing, invalid, or revoked.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 403 · `forbidden`

The key can see the resource but lacks permission for this operation.

Use a key with the required action or scope kind.

```json
{
  "error": {
    "code": "forbidden",
    "message": "The key can see the resource but lacks permission for this operation.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 404 · `not_found`

The resource is absent, deleted, or outside this key’s scope.

Check the identifier and the key’s namespace and collection scope.

```json
{
  "error": {
    "code": "not_found",
    "message": "The resource is absent, deleted, or outside this key’s scope.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 409 · `already_exists`

The requested resource name already exists.

Use a different key name, or list the existing keys before continuing.

```json
{
  "error": {
    "code": "already_exists",
    "message": "The requested resource name already exists.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 413 · `request_too_large`

The JSON request body exceeds 1,048,576 bytes.

Reduce the JSON body. Use upload or a URI source for larger file content.

```json
{
  "error": {
    "code": "request_too_large",
    "message": "The JSON request body exceeds 1,048,576 bytes.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 503 · `temporarily_unavailable`

A dependency, cluster readiness condition, or capacity limit prevents this operation.

Inspect retryable and details. Retry only when permitted, preserving the idempotency key for the same write.

```json
{
  "error": {
    "code": "temporarily_unavailable",
    "message": "A dependency, cluster readiness condition, or capacity limit prevents this operation.",
    "request_id": "req_example",
    "retryable": true,
    "details": {}
  }
}
```

### 500 · `internal_error`

An unexpected server failure prevented this request from completing.

Retain x-request-id for investigation. Follow retryable before retrying a write.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected server failure prevented this request from completing.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

## Other request languages

### cURL

```bash
curl -sS -X POST "$GRAPHON_ENGINE_URL/keys" \
  -H "Authorization: Bearer $GRAPHON_ENGINE_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-raw '{
  "name": "acme-read",
  "scope": {
    "kind": "namespace",
    "namespace": "acme",
    "actions": [
      "read",
      "search",
      "query"
    ]
  }
}'
```

### Python

```python
import json
import os
import urllib.request

request = urllib.request.Request(
    os.environ["GRAPHON_ENGINE_URL"] + "/keys",
    data=json.dumps({"name": "acme-read", "scope": {"kind": "namespace", "namespace": "acme", "actions": ["read", "search", "query"]}}).encode("utf-8"),
    headers={
        "Authorization": "Bearer " + os.environ["GRAPHON_ENGINE_API_KEY"],
        "Content-Type": "application/json",
    },
    method="POST",
)
with urllib.request.urlopen(request) as response:
    print(response.read().decode())
```

### JavaScript

```javascript
const response = await fetch(process.env.GRAPHON_ENGINE_URL + "/keys", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.GRAPHON_ENGINE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({"name":"acme-read","scope":{"kind":"namespace","namespace":"acme","actions":["read","search","query"]}}),
});
console.log(response.status, await response.text());
```

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
