> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a KMS key

> Provision a fresh key under the requested spec and usage.
Synchronous: the response carries the key in state=enabled,
ready for crypto operations.




## OpenAPI

````yaml /api-reference/specs/kms.yaml post /v1/keys
openapi: 3.0.3
info:
  title: Basaltic KMS API
  version: 1.0.0
  description: |
    Customer-managed encryption keys. Encrypt and decrypt directly, or let
    another service — secrets, storage, images — wrap its data keys with one
    of yours.
  contact:
    name: Basaltic Support
    email: ping@basaltic.sh
  license:
    name: Proprietary
    url: https://basaltic.sh/terms
servers:
  - url: https://kms.{region}.basaltic.sh
    description: Regional API endpoint
    variables:
      region:
        default: sa-saopaulo-1
        description: Region code
security:
  - SignatureAuth: []
paths:
  /v1/keys:
    post:
      tags:
        - KMS
      summary: Create a KMS key
      description: |
        Provision a fresh key under the requested spec and usage.
        Synchronous: the response carries the key in state=enabled,
        ready for crypto operations.
      operationId: createKey
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateKeyRequest'
      responses:
        '201':
          description: Key created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KeyResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          description: Key name already in use
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - SignatureAuth: []
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      description: >-
        Optional client-generated key that makes a create replay-safe. Retrying
        a request with the same key returns the original outcome verbatim
        instead of creating a duplicate resource. Reusing a key with a different
        request body is rejected (422); a request whose key is still being
        processed returns 409. Records are honored for 24 hours. Use a UUID or
        similarly unique token.
      required: false
      schema:
        type: string
        maxLength: 255
      example: 550e8400-e29b-41d4-a716-446655440000
  schemas:
    CreateKeyRequest:
      type: object
      required:
        - name
        - key_spec
      properties:
        name:
          type: string
          pattern: ^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$
          description: |
            Unique per account. Surfaces in the CRN
            (crn:kms:<region>:<account>:key/<name>) — letters, digits,
            dot, dash, underscore.
          example: prod-master
        description:
          type: string
          example: Master KEK for the production data plane
        tags:
          $ref: '#/components/schemas/Tags'
        key_spec:
          $ref: '#/components/schemas/KeySpec'
        key_usage:
          allOf:
            - $ref: '#/components/schemas/KeyUsage'
          description: |
            Required for RSA specs (both encrypt_decrypt and sign_verify
            are valid). Defaults to encrypt_decrypt for AES, sign_verify
            for ECDSA.
    KeyResponse:
      type: object
      properties:
        key:
          $ref: '#/components/schemas/Key'
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
            - request_id
          properties:
            code:
              type: string
              description: Error code identifying the type of error
              example: INVALID_INPUT
            message:
              type: string
              description: Human-readable error message
              example: Invalid request parameters
            request_id:
              type: string
              format: uuid
              description: Request ID for debugging
              example: 550e8400-e29b-41d4-a716-446655440000
    Tags:
      type: object
      additionalProperties:
        type: string
      example:
        environment: production
        team: backend
    KeySpec:
      type: string
      enum:
        - aes-256
        - rsa-2048
        - rsa-4096
        - ecdsa-p256
      description: |
        Cryptographic spec. Determines which mechanisms apply:
          - aes-256:     symmetric AEAD (AES-GCM)
          - rsa-2048:    asymmetric; OAEP-SHA256 encrypt + PSS-SHA256 sign
          - rsa-4096:    same as rsa-2048
          - ecdsa-p256:  asymmetric; SHA-256 sign/verify (no encrypt)
      example: aes-256
    KeyUsage:
      type: string
      enum:
        - encrypt_decrypt
        - sign_verify
      description: |
        Operation set the key is pinned to at create time. Even when the
        spec supports both (RSA), one usage must be chosen — the service
        refuses operations from the other set.
      example: encrypt_decrypt
    Key:
      type: object
      required:
        - id
        - crn
        - name
        - key_spec
        - key_usage
        - state
        - tags
        - created_at
        - updated_at
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          example: 3f8a1c2e-9b47-4d6a-bc11-0e2f5a7d9c41
        crn:
          type: string
          readOnly: true
          example: crn:kms:sa-saopaulo-1:my-account:key/prod-master
        name:
          type: string
          example: prod-master
        description:
          type: string
          example: Master KEK for the production data plane
        tags:
          $ref: '#/components/schemas/Tags'
        key_spec:
          $ref: '#/components/schemas/KeySpec'
        key_usage:
          $ref: '#/components/schemas/KeyUsage'
        state:
          $ref: '#/components/schemas/KeyState'
        system:
          type: boolean
          readOnly: true
          example: true
          description: |
            Present and true on platform-owned envelope keys (credential
            master, JWT signer, …), which are visible but not yours to
            operate on. Omitted on customer keys. Console and CLI read it
            to hide the destructive actions.
        deletion_scheduled_at:
          type: string
          format: date-time
          nullable: true
          example: '2026-01-22T09:30:00Z'
          description: |
            Set only while state=pending_deletion. The key (and its
            cryptographic material) is hard-deleted once now() reaches
            this timestamp; CancelKeyDeletion before then returns the
            key to state=disabled.
        created_at:
          type: string
          format: date-time
          readOnly: true
          example: '2026-01-15T09:30:00Z'
        updated_at:
          type: string
          format: date-time
          readOnly: true
          example: '2026-01-15T09:30:00Z'
    KeyState:
      type: string
      enum:
        - enabled
        - disabled
        - pending_deletion
      description: |
        Lifecycle state:
          - enabled:          usable for spec/usage operations
          - disabled:         exists; refuses crypto ops; can be re-enabled
          - pending_deletion: scheduled for hard delete at
                              deletion_scheduled_at; cancellable until then
      example: enabled
  responses:
    BadRequest:
      description: Invalid request parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INVALID_INPUT
              message: Invalid request parameters
              request_id: 550e8400-e29b-41d4-a716-446655440000
    Unauthorized:
      description: Authentication required or token invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: UNAUTHORIZED
              message: Authentication required
              request_id: 550e8400-e29b-41d4-a716-446655440000
    Forbidden:
      description: Insufficient permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: ACCESS_DENIED
              message: You don't have permission to perform this action
              request_id: 550e8400-e29b-41d4-a716-446655440000
    UnprocessableEntity:
      description: |
        The request is well-formed but cannot be processed as sent. On the
        operations that accept `Idempotency-Key` this is the key-reuse case: the
        key was first seen with a different request payload, so replaying the
        stored outcome would answer a question the caller did not ask.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: IDEMPOTENCY_KEY_REUSED
              message: >-
                This Idempotency-Key was already used with a different request
                payload
              request_id: 550e8400-e29b-41d4-a716-446655440000
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INTERNAL_ERROR
              message: An internal error occurred
              request_id: 550e8400-e29b-41d4-a716-446655440000
  securitySchemes:
    SignatureAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >
        Request signing with an access key issued to a service account. An

        HMAC-SHA256 over a canonical form of the request, close to AWS SigV4.

        The `basaltic` CLI signs for you.


        Send `Authorization`, `X-Date` (UTC, `YYYYMMDDTHHMMSSZ`) and `X-Nonce`

        (random per request); add `X-Content-Sha256` to bind a body, and

        `X-Amz-Security-Token` when using temporary credentials.


        ```

        Authorization: BASALTIC-HMAC-SHA256
        Credential=<access_key_id>/<date>/<region>/basaltic/basaltic_request,
        SignedHeaders=host;x-date;x-nonce, Signature=<hex>

        ```


        `<region>` is the region code you are calling, or `global` for the
        global

        services. A signature is valid for 5 minutes from `X-Date`, and mutating

        requests are replay-guarded on the nonce.


        **Full signing procedure, including a working implementation:**

        https://docs.basaltic.sh/authentication


        ## Rate limits

        There is no global request budget. A limit applies only where an

        operation documents a `429`, and that operation says what it counts.

        Those responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining`,

        `X-RateLimit-Reset` and, on a `429`, `Retry-After` — read them rather

        than hard-coding a number. Retrying before `Retry-After` is refused and

        extends the window. Everything else is bounded by quota, not by request

        rate.

````