API Keys
Python SDK reference for API keys: creation, lookup, listing, updates, and deletion.
Methods on this page are called as client.apikeys.<method>(...) where client is either a synchronous Goodmem or asynchronous AsyncGoodmem instance initialized below:
from goodmem import Goodmem
client = Goodmem(base_url='http://localhost:8080', api_key='gm_...')from goodmem import AsyncGoodmem
client = AsyncGoodmem(base_url='http://localhost:8080', api_key='gm_...')Create a new API key
apikeys.create(*, api_key_id: str = None, authority_mode: ApiKeyAuthorityMode = None, ceiling: list[AccessPolicyRule] = None, expires_at: int = None, labels: dict[str, str] = None, subject_principal_id: str = None, valid_from: int = None) → CreateApiKeyResponse
Issues a new API key and returns its raw value exactly once. Omitted subject and authority_mode create a self-issued human key that inherits the subject's live authority. A SCOPED key requires a nonempty immutable ceiling, and every ceiling rule must be conservatively covered by both the subject's live authority and the issuing credential's effective authority. A scoped issuer can create only scoped children.
SERVICE subjects require SCOPED mode and MANAGE_ACCESS on the service identity.
AUTHORIZATION: Requires CREATE_API_KEY on the proposed credential; subject and ceiling checks are repeated by the final insertion statement.
Parameters:
- api_key_id (
str, format:uuid, optional) — Optional client-provided UUID for idempotent creation. If not provided, server generates a new UUID. Returns ALREADY_EXISTS if ID is already in use. - authority_mode (
ApiKeyAuthorityMode, optional, server default='INHERIT_SUBJECT') — Authority mode. Omit to create a self-issued human key that inherits live authority. A scoped issuing credential may create only SCOPED children. - ceiling (
list[AccessPolicyRule], optional) — Immutable authorization ceiling. Required and nonempty for SCOPED; omitted for INHERIT_SUBJECT, with at most 1,000 rules. Every rule must be covered by both the subject's live authority and the issuing credential's effective authority. - expires_at (
int, format:int64, optional) — Exclusive expiration timestamp in milliseconds since epoch. It must be later than valid_from, which defaults to issuance time; if omitted, the key does not expire. - labels (
dict[str, str], optional) — Key-value pairs of metadata associated with the API key. Used for organization and filtering. At most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-]. - subject_principal_id (
str, format:uuid, optional) — Principal authenticated by this key. Omit to use the authenticated principal. - valid_from (
int, format:int64, optional) — Inclusive activation time in epoch milliseconds. Omit to activate at issuance time.
Returns:
CreateApiKeyResponse
Raises:
PermissionDeniedError— The caller lacks the permission the operation requires.ConflictError— The resource already exists or conflicts with existing state.APIError— Any other non-2xx HTTP response (base class; exposesstatus_codeandbody).
Example
key = client.apikeys.create(
labels={"env": "docs", "purpose": "sdk-doc-test"},
)Get an API key
apikeys.get(*, id: str) → ApiKeyResponse
Returns complete non-secret metadata for one existing credential after requiring effective READ_API_KEY authority. The immutable ceiling is always complete and ceiling_omitted is false. Raw key material and hashes are never returned.
Parameters:
- id (
str) — API-key UUID
Returns:
ApiKeyResponse — Returns the API key metadata.
Raises:
APIError— Any other non-2xx HTTP response (base class; exposesstatus_codeandbody).
Example
key = client.apikeys.get(id="your-api-key-id")
print(key.api_key_id, key.lifecycle_state)List API keys
apikeys.list(*, lifecycle_state: str = None, max_results: int = None, next_token: str = None, owner_principal_id: str = None, subject_principal_id: str = None, view: str = None) → Page[ApiKeyResponse]
Requires LIST_API_KEY on the singleton instance, then retrieves one UUID-ordered page containing only credentials that independently pass READ_API_KEY. Both gates use the authenticated principal's live authority and any scoped-key ceiling. Subject, owner, and lifecycle filters are applied after authorization.
FULL includes complete immutable ceilings; BASIC omits them, sets ceiling_omitted=true, and permits larger pages. Raw key values and key hashes are never returned.
Parameters:
- lifecycle_state (
str, optional) — Filter by precise lifecycle state - max_results (
int, format:int32, optional) — Page size; FULL defaults to 10 and permits at most 20, while BASIC defaults to 50 and permits at most 1,000 - next_token (
str, optional) — Opaque continuation token returned by the preceding page - owner_principal_id (
str, optional) — Filter by exact administrative-owner UUID - subject_principal_id (
str, optional) — Filter by exact subject-principal UUID - view (
str, optional, server default='FULL') — Metadata projection; omission defaults to FULL
Returns:
Page[ApiKeyResponse]
Raises:
PermissionDeniedError— The caller lacks the permission the operation requires.APIError— Any other non-2xx HTTP response (base class; exposesstatus_codeandbody).
Example
for key in client.apikeys.list():
print(key.api_key_id, key.status)Update an API key
apikeys.update(*, id: str, request: UpdateApiKeyRequest | dict) → ApiKeyResponse
Updates an existing API key's labels or lifecycle status. Key ID, subject, ownership, key material, validity window, and creation audit fields remain immutable. Label changes require UPDATE_API_KEY; setting status=INACTIVE permanently revokes the key and requires DELETE_API_KEY; a request doing both requires both operations. Revoked keys cannot be reactivated. Side effects include updating administrative audit fields and, for revocation, recording the revocation time and actor.
Parameters:
- id (
str) — The unique identifier of the resource to update. - request (
UpdateApiKeyRequest | dict) — The update payload. Accepts a UpdateApiKeyRequest instance or a plain dict with the same fields. Only specified fields will be modified.
Returns:
ApiKeyResponse — Returns the API key metadata.
Raises:
PermissionDeniedError— The caller lacks the permission the operation requires.APIError— Any other non-2xx HTTP response (base class; exposesstatus_codeandbody).
Example
from goodmem.types import UpdateApiKeyRequest
# Option 1: typed request object
updated = client.apikeys.update(id="your-api-key-id", request=UpdateApiKeyRequest(
merge_labels={"updated": "true"},
))
assert updated.api_key_id == "your-api-key-id"
# Option 2: plain dict (validated via pydantic)
updated = client.apikeys.update(id="your-api-key-id", request={
"merge_labels": {"updated": "true"},
})
assert updated.api_key_id == "your-api-key-id"Delete an API key
apikeys.delete(*, id: str) → None
Permanently revokes an API key and immediately rejects it for future authentication. The durable credential and audit history remain stored. This operation requires DELETE_API_KEY and records the revocation time and actor; it cannot be undone.
PUT /v1/apikeys/{id} with status=INACTIVE performs the same permanent revocation.
Parameters:
- id (
str) — The UUID of the API key to delete
Returns:
None
Raises:
PermissionDeniedError— The caller lacks the permission the operation requires.APIError— Any other non-2xx HTTP response (base class; exposesstatus_codeandbody).
Example
client.apikeys.delete(id="your-api-key-id")Async usage: client.apikeys exposes the same methods on AsyncGoodmem; use await / async for as needed.
Data Models
All data models are pydantic v2 models. Fields are shown with their Python attribute names; JSON responses use camelCase aliases (e.g., owner_id → ownerId).
CreateApiKeyResponse
Response returned when creating a new API key.
- api_key_metadata (
ApiKeyResponse, optional) — Metadata for the created API key. - raw_api_key (
str, optional) — The actual API key value. This is only returned once and cannot be retrieved again.
UpdateApiKeyRequest
Request parameters for updating an API key.
- status (
Literal['ACTIVE', 'INACTIVE'], optional) — New status for the API key. INACTIVE is permanent; revoked keys cannot be reactivated. - replace_labels (
dict[str, str], optional) — Replace all existing labels with this set. Mutually exclusive with merge_labels. The stored map may contain at most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-]. - merge_labels (
dict[str, str], optional) — Merge these labels with existing ones. Mutually exclusive with replace_labels. The final stored map may contain at most 20 entries; keys and values contain at most 255 characters; keys use [a-z0-9._-].