---
title: "Create or update an API-key credential"
method: POST
path: "/v1/credentials/api-key"
tags: ["Credentials"]
---

# Create or update an API-key credential

`POST /v1/credentials/api-key`

## Request

- Base URL: `http://localhost:9998`
- URL: `http://localhost:9998/v1/credentials/api-key`
- Auth: HTTP bearer

Create from a template (templateId) or custom host, optionally storing an API key in the Keychain. One credential per (host, resource).

## Request body

- object
  - `templateId` string: Template ID (openai, anthropic, cloudflare, etc.) — mutually exclusive with host
  - `host` string: Custom host — mutually exclusive with templateId
  - `name` string
  - `apiKey` string: API key (stored once per credential in Keychain)
  - `accountId` string: Account identifier used in URL paths

## Response `200`

The created or updated credential

- Credential: Unified credential — one provider entry aggregating optional API-key and OAuth materials. Presence and status only; secret values never leave ToolPiper.
  - `id` string, required: Stable, non-colliding identity key (host:<host> or provider:<id>).
  - `providerId` string, nullable: Catalog provider id when known (google, openai, ...).
  - `displayName` string, required
  - `keyPresence` 'configured' | 'absent' | 'unreadable', required: Whether an API key is stored in the Keychain for this credential, three-way. `configured` means the Keychain returned a key. `absent` means the Keychain answered and there is no key for this account. `unreadable` means the Keychain refused to answer (locked, or a missing entitlement) and says nothing about whether a key exists, so consumers must not treat it as absence.
  - `transport` object, nullable: Host plus URL-placeholder fields for an API-key credential.
    - `host` string
    - `templateId` string, nullable
    - `accountId` string, nullable
    - `deployment` string, nullable
    - `projectId` string, nullable
    - `region` string, nullable
    - `apiVersion` string, nullable
    - `resource` string, nullable
  - `oauth` OAuthGrantStatus, nullable: OAuth grant status carried on a credential. Status only, never tokens.
    - `authenticated` boolean, required
    - `scopes` string[], required
    - `accountEmail` string, nullable
    - `authMethod` 'browser' | 'serviceAccount', nullable
    - `connectedAt` string, nullable: ISO-8601 timestamp.
    - `lastRefresh` string, nullable: ISO-8601 timestamp.

## Other responses

- `400`: Invalid request

---

[API](https://skmtc.dev/modelpiper/apis/toolpiper-api.md) · [All operations](https://skmtc.dev/modelpiper/apis/toolpiper-api/llms.txt) · [OpenAPI document](https://skmtc.dev/modelpiper/apis/toolpiper-api/revisions/6722bf56385c?raw)
