# Create project key

Create a new project API key. The raw secret is returned once and cannot be retrieved again.

Endpoint: POST /projects/{project_id}/keys
Version: 0.23.8
Security: Auth

## Path parameters:

  - `project_id` (string, required)
    Unique identifier (UUID) of the project.
    Example: "550e8400-e29b-41d4-a716-446655440000"

## Request fields (application/json):

  - `name` (string, required)
    Human-readable key name.
    Example: "Backend service key"

  - `permissions` (array, required)
    Permissions to grant. Must all belong to the api:* scope.
    Enum: "api:address:read", "api:address:write", "api:address:delete", "api:transaction:read", "api:blockchain:read", "api:balance:read", "api:asset:read", "api:asset:write", "api:simulate:call", "api:subscription:read", "api:subscription:write", "api:subscription:delete", "api:channel:read", "api:channel:write", "api:channel:delete", "api:public_key:read", "api:public_key:write", "api:public_key:delete", "api:invoice:read", "api:invoice:write", "api:invoice:delete", "api:token_group:read", "api:token_group:write", "api:token_group:delete", "api:address_group:read", "api:address_group:write", "api:address_group:delete"

  - `expires_at` (string)
    Optional expiration timestamp. Omit to create a non-expiring key.
    Example: "2025-01-15T10:30:00Z"

## Response 201 fields (application/json):

  - `item` (object, required)
    A project-scoped API key. The raw secret is only shown at creation.

  - `item.id` (string, required)
    Unique key identifier.

  - `item.name` (string, required)
    Human-readable key name.

  - `item.key_preview` (string, required)
    First 6 characters of the random body, i.e. the 6 characters immediately after the vilna_{type}_ prefix. For raw key vilna_api_a1B2c3D4e5F6g7H8i9J0k1L2m3N4o5 the preview is a1B2c3. Safe to display in UIs for identifying a key without exposing the secret.

  - `item.permissions` (array, required)
    Raw permission strings granted to the key.
    Enum: "api:address:read", "api:address:write", "api:address:delete", "api:transaction:read", "api:blockchain:read", "api:balance:read", "api:asset:read", "api:asset:write", "api:simulate:call", "api:subscription:read", "api:subscription:write", "api:subscription:delete", "api:channel:read", "api:channel:write", "api:channel:delete", "api:public_key:read", "api:public_key:write", "api:public_key:delete", "api:invoice:read", "api:invoice:write", "api:invoice:delete", "api:token_group:read", "api:token_group:write", "api:token_group:delete", "api:address_group:read", "api:address_group:write", "api:address_group:delete"

  - `item.created_at` (string, required)
    Timestamp when the resource was created. RFC 3339 / ISO 8601, UTC.

  - `item.expires_at` (string)
    Timestamp when the resource expires. RFC 3339 / ISO 8601, UTC. Absent if the resource never expires.

  - `item.revoked_at` (string)
    Timestamp when the resource was revoked. RFC 3339 / ISO 8601, UTC. Absent if the resource is active.

  - `item.last_used_at` (string)
    Timestamp of the most recent successful authentication with this key. RFC 3339 / ISO 8601, UTC. Absent until the key has been used at least once.

  - `item.workspace_id` (string, required)
    Workspace the key's project belongs to. Populated for convenience; always matches the project's workspace.

  - `item.project_id` (string, required)
    Project this key is scoped to.

  - `raw_key` (string, required)
    The full secret key, 40 characters: vilna_{type}_{body} where {type}
is api, mgt, or rpc and {body} is 30 base62 characters.
Returned only in the create response; store it securely. The server
does not store the secret and cannot return it again; lost keys must be
revoked and re-created.

## Response 400 fields (application/problem+json):

  - `type` (string, required)
    A URI that identifies the error type.
Open it in a browser to read about this category of error.
    Example: "https://docs.vilna.io/apis/problems/invalid-request"

  - `title` (string, required)
    A short summary of the error type.
Use detail for information specific to this occurrence.
    Example: "Invalid Request"

  - `status` (integer, required)
    The HTTP status code for this error.
Matches the status code of the HTTP response.
    Example: 400

  - `detail` (string)
    A human-readable explanation of what went wrong in this specific case.
May be localized.
    Example: "Validation error"

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.

  - `code` (string, required)
    Stable machine-readable error code ({domain}.{reason}) for programmatic error handling. Unlike the HTTP status or free-form detail, this code is guaranteed not to change between versions for a given error condition, so it is safe to branch on in client code. Defaults to unspecified when the server has not assigned a specific code.
    Example: "blockchain.name_too_long"

  - `fields` (array)
    List of invalid fields in the request

  - `fields.name` (string, required)
    The name of the invalid field
    Example: "meta"

  - `fields.reason` (string, required)
    Why this field is invalid
    Example: "Exceeded maximum data size — must not exceed 1000 characters"

## Response 401 fields (application/problem+json):

  - `type` (string, required)
    A URI that identifies the error type.
Open it in a browser to read about this category of error.
    Example: "https://docs.vilna.io/apis/problems/unauthorized"

  - `title` (string, required)
    A short summary of the error type.
Use detail for information specific to this occurrence.
    Example: "Unauthorized"

  - `status` (integer, required)
    The HTTP status code for this error.
Matches the status code of the HTTP response.
    Example: 401

  - `detail` (string)
    A human-readable explanation of what went wrong in this specific case.
May be localized.
    Example: "Missing or invalid authentication credentials"

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.

  - `code` (string, required)
    Stable machine-readable error code ({domain}.{reason}) for programmatic error handling. Unlike the HTTP status or free-form detail, this code is guaranteed not to change between versions for a given error condition, so it is safe to branch on in client code. Defaults to unspecified when the server has not assigned a specific code.
    Example: "auth.unauthorized"

## Response 403 fields (application/problem+json):

  - `type` (string, required)
    A URI that identifies the error type.
Open it in a browser to read about this category of error.
    Example: "https://docs.vilna.io/apis/problems/forbidden"

  - `title` (string, required)
    A short summary of the error type.
Use detail for information specific to this occurrence.
    Example: "Forbidden"

  - `status` (integer, required)
    The HTTP status code for this error.
Matches the status code of the HTTP response.
    Example: 403

  - `detail` (string)
    A human-readable explanation of what went wrong in this specific case.
May be localized.
    Example: "You do not have permission to perform this action"

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.

  - `code` (string, required)
    Stable machine-readable error code ({domain}.{reason}) for programmatic error handling. Unlike the HTTP status or free-form detail, this code is guaranteed not to change between versions for a given error condition, so it is safe to branch on in client code. Defaults to unspecified when the server has not assigned a specific code.
    Example: "chain.not_allowed"

## Response 404 fields (application/problem+json):

  - `type` (string, required)
    A URI that identifies the error type.
Open it in a browser to read about this category of error.
    Example: "https://docs.vilna.io/apis/problems/not-found"

  - `title` (string, required)
    A short summary of the error type.
Use detail for information specific to this occurrence.
    Example: "Not Found"

  - `status` (integer, required)
    The HTTP status code for this error.
Matches the status code of the HTTP response.
    Example: 404

  - `detail` (string)
    A human-readable explanation of what went wrong in this specific case.
May be localized.
    Example: "The requested resource was not found"

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.

  - `code` (string, required)
    Stable machine-readable error code ({domain}.{reason}) for programmatic error handling. Unlike the HTTP status or free-form detail, this code is guaranteed not to change between versions for a given error condition, so it is safe to branch on in client code. Defaults to unspecified when the server has not assigned a specific code.
    Example: "blockchain.not_found"

## Response 422 fields (application/problem+json):

  - `type` (string, required)
    A URI that identifies the error type.
Open it in a browser to read about this category of error.
    Example: "https://docs.vilna.io/apis/problems/precondition-failed"

  - `title` (string, required)
    A short summary of the error type.
Use detail for information specific to this occurrence.
    Example: "Precondition Failed"

  - `status` (integer, required)
    The HTTP status code for this error.
Matches the status code of the HTTP response.
    Example: 422

  - `detail` (string)
    A human-readable explanation of what went wrong in this specific case.
May be localized.
    Example: "The request cannot be processed due to failed business rule validation"

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.

  - `code` (string, required)
    Stable machine-readable error code ({domain}.{reason}) for programmatic error handling. Unlike the HTTP status or free-form detail, this code is guaranteed not to change between versions for a given error condition, so it is safe to branch on in client code. Defaults to unspecified when the server has not assigned a specific code.
    Example: "simulation.failed"

  - `fields` (array)
    List of fields that failed precondition validation

  - `fields.name` (string, required)
    The name of the field that failed validation
    Example: "status"

  - `fields.reason` (string, required)
    Why the precondition failed for this field
    Example: "Cannot transition from archived to active state"

## Response default fields (application/problem+json):

  - `type` (string, required)
    A URI that identifies the error type.
Open it in a browser to read about this category of error.

  - `title` (string, required)
    A short summary of the error type.
Use detail for information specific to this occurrence.

  - `status` (integer, required)
    The HTTP status code for this error.
Matches the status code of the HTTP response.

  - `detail` (string)
    A human-readable explanation of what went wrong in this specific case.
May be localized.

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.

  - `code` (string, required)
    Stable machine-readable error code ({domain}.{reason}) for programmatic error handling. Unlike the HTTP status or free-form detail, this code is guaranteed not to change between versions for a given error condition, so it is safe to branch on in client code. Defaults to unspecified when the server has not assigned a specific code.


