# Create channel

Create a channel. The configuration schema is selected by `config.kind`. Retry the same request
with the same `Idempotency-Key` to replay its original completed response under current
authorization: ordinary responses are available for 24 hours from the original completion; a
response containing a webhook signing secret is available for 60 seconds from that completion
and only while the secret is current. A new key represents a new intent and may create another
channel; there is no deduplication by name or configuration. See the common mutation idempotency
policy for expiry and conflict behavior. Send `max_attempts` explicitly; the server does not
supply the documented default.

Endpoint: POST /channels
Version: 2.0.0
Security: Auth

## Security:

  - `Auth` (unknown)
    apiKey in header X-Api-Key scopes: api:channel:write

## Header parameters:

  - `Idempotency-Key` (string, required)
    Case-sensitive opaque key for one mutation intent. Send exactly one header occurrence containing 1 to 128 visible ASCII bytes (0x21..0x7e). Commas are literal data; do not trim, lowercase or require UUID syntax. Reuse the same key for retries, with current authorization checked on every attempt. Repeated header occurrences are rejected.

## Request fields (application/json):

  - `name` (string, required)
    Display name for the notification channel.
    Example: Production Alerts Webhook

  - `config` (any, required)
    Channel-specific configuration for channel creation. The Telegram
`bot_token` is required in plaintext; after that it's only ever returned
masked. A webhook configuration must state `payload_version`; a Telegram
configuration that carries it is rejected.

  - `config.kind` (string, required)
    Configuration type discriminator.
    Enum: "telegram", "webhook"

  - `config.bot_token` (string, required)
    Telegram Bot API token from @BotFather. Format: `{bot_id}:{auth_token}`.
Accepted on write (create, or update to rotate the token). On read the
value is masked (see `TelegramBotTokenMasked`).

  - `config.chat_id` (integer, required)
    Telegram chat ID where notifications are sent.
Positive for private chats, negative for groups, `-100...` prefix for channels.
Use the Telegram Bot API `getUpdates` method to find your chat ID.

  - `config.language` (string, required)
    Language code for localization.
    Enum: "ru", "en"

  - `config.thread_id` (integer, required)
    Unique identifier for the target message thread (topic) of a forum.

Used for forum supergroups and private chats of bots with forum topic mode enabled only.
Default value is 0 (no specific thread).
    Example: 0

  - `config.url` (string, required)
    HTTP/HTTPS endpoint that accepts POST requests with JSON payloads.
Use headers (not query params) for authentication.

  - `config.headers` (object)
    Optional HTTP headers to include with webhook requests, including
receiver authentication headers such as `X-Webhook-Secret`.
Anyone authorized to read the channel can read these values; they are
not masked. The webhook signing secret is managed separately and
produces the `X-Webhook-Signature` header. Custom headers cannot override
signing or event headers. Other names starting with `X-Webhook-` are
reserved; header names are case-insensitive.
On update, omit `headers` to keep the stored set, send an object to
replace it, or `{}` to clear them.
    Example: {"X-Tenant-Id":"acme"}

  - `config.payload_version` (string, required)
    Webhook body contract a channel sends. It names the complete payload shape, not the HTTP route
and not the signature algorithm. Webhook channels created through v2 state it explicitly.
    Enum: "v1", "v2"

  - `max_attempts` (integer, required)
    Automatic delivery attempts allowed for one notification, counting the first one. Manual
resends are not charged against it.
    Example: 3

  - `is_enabled` (boolean)
    Whether the channel starts out delivering notifications.

## Request examples:

  - `Telegram channel` (unknown)

  - `Webhook channel` (unknown)

  - `Webhook channel created switched off` (unknown)

## Response 200:

  - `200` (unknown)
    Channel created.

## Response 200 fields (application/json):

  - `item` (object, required)
    Notification delivery channel.
`disabled_reason` is null while `is_enabled` is true and names the cause once the channel has
been disabled. `config.kind` always equals the channel's `kind`, which is fixed at creation.
    Example: {"id":"550e8400-e29b-41d4-a716-446655440001","kind":"telegram","name":"Production Alerts","is_enabled":true,"disabled_reason":null,"max_attempts":3,"config":{"kind":"telegram","bot_token":"123456789:•…

  - `item.id` (string, required)
    Unique identifier (UUID).
    Example: 550e8400-e29b-41d4-a716-446655440000

  - `item.kind` (string, required)
    Type of notification delivery channel.
    Enum: "telegram", "webhook"

  - `item.name` (string, required)
    Display name for the notification channel.
    Example: Production Alerts Webhook

  - `item.is_enabled` (boolean, required)
    Whether the channel currently delivers notifications.

  - `item.disabled_reason` (any, required)
    Cause of the current disabled state, or null while is_enabled is true.

  - `item.max_attempts` (integer, required)
    Automatic delivery attempts allowed for one notification, counting the first one. Manual
resends are not charged against it.
    Example: 3

  - `item.config` (any, required)
    Channel-specific configuration as returned in responses. Schema depends
on `kind`: `telegram` (bot_token + chat_id) or `webhook` (url + headers).
The Telegram `bot_token` is masked; webhook headers are returned verbatim.
`config.kind` always equals the channel's own `kind`, and `payload_version`
appears on webhook configurations only.

  - `item.config.kind` (string, required)
    Configuration type discriminator.
    Enum: "telegram", "webhook"

  - `item.config.bot_token` (string, required)
    Masked Telegram bot token returned in responses. The `bot_id` prefix is
preserved for identification; the secret `auth_token` portion is hidden,
showing only its last 4 characters: `{bot_id}:••••<last4>`.
To keep the stored token on update, omit `bot_token` from the request.
To rotate it, send a new plaintext token (see `TelegramBotToken`).

  - `item.config.chat_id` (integer, required)
    Telegram chat ID where notifications are sent.
Positive for private chats, negative for groups, `-100...` prefix for channels.
Use the Telegram Bot API `getUpdates` method to find your chat ID.

  - `item.config.language` (string, required)
    Language code for localization.
    Enum: "ru", "en"

  - `item.config.thread_id` (integer, required)
    Unique identifier for the target message thread (topic) of a forum.

Used for forum supergroups and private chats of bots with forum topic mode enabled only.
Default value is 0 (no specific thread).
    Example: 0

  - `item.config.url` (string, required)
    HTTP/HTTPS endpoint that accepts POST requests with JSON payloads.
Use headers (not query params) for authentication.

  - `item.config.headers` (object)
    Optional HTTP headers to include with webhook requests, including
receiver authentication headers such as `X-Webhook-Secret`.
Anyone authorized to read the channel can read these values; they are
not masked. The webhook signing secret is managed separately and
produces the `X-Webhook-Signature` header. Custom headers cannot override
signing or event headers. Other names starting with `X-Webhook-` are
reserved; header names are case-insensitive.
On update, omit `headers` to keep the stored set, send an object to
replace it, or `{}` to clear them.
    Example: {"X-Tenant-Id":"acme"}

  - `item.config.payload_version` (string, required)
    Webhook body contract a channel sends. It names the complete payload shape, not the HTTP route
and not the signature algorithm. Webhook channels created through v2 state it explicitly.
    Enum: "v1", "v2"

  - `item.created_at` (string, required)
    Timestamp when the resource was created.
    Example: 2024-01-15T10:30:00Z

  - `item.updated_at` (string, required)
    Timestamp when the resource was last updated.
    Example: 2024-01-15T10:30:00Z

  - `webhook_secret` (string)
    The full webhook signing secret, 55 characters: `vilna_whsec_{body}`
where `{body}` is 43 ASCII base62 characters. Used as the HMAC-SHA256 key
for the `X-Webhook-Signature` header on outgoing webhook deliveries.
**Returned only at channel creation and on rotation; store it
securely.** Lost secrets must be rotated.
    Example: vilna_whsec_a3K9pQ2mB5N8rT1xY7cZ4dH6eJ0fLm0123456789ABC

## Response 201:

  - `201` (unknown)
    Channel created.

## Response 201 fields (application/json):

  - `item` (object, required)
    Notification delivery channel.
`disabled_reason` is null while `is_enabled` is true and names the cause once the channel has
been disabled. `config.kind` always equals the channel's `kind`, which is fixed at creation.
    Example: {"id":"550e8400-e29b-41d4-a716-446655440001","kind":"telegram","name":"Production Alerts","is_enabled":true,"disabled_reason":null,"max_attempts":3,"config":{"kind":"telegram","bot_token":"123456789:•…

  - `item.id` (string, required)
    Unique identifier (UUID).
    Example: 550e8400-e29b-41d4-a716-446655440000

  - `item.kind` (string, required)
    Type of notification delivery channel.
    Enum: "telegram", "webhook"

  - `item.name` (string, required)
    Display name for the notification channel.
    Example: Production Alerts Webhook

  - `item.is_enabled` (boolean, required)
    Whether the channel currently delivers notifications.

  - `item.disabled_reason` (any, required)
    Cause of the current disabled state, or null while is_enabled is true.

  - `item.max_attempts` (integer, required)
    Automatic delivery attempts allowed for one notification, counting the first one. Manual
resends are not charged against it.
    Example: 3

  - `item.config` (any, required)
    Channel-specific configuration as returned in responses. Schema depends
on `kind`: `telegram` (bot_token + chat_id) or `webhook` (url + headers).
The Telegram `bot_token` is masked; webhook headers are returned verbatim.
`config.kind` always equals the channel's own `kind`, and `payload_version`
appears on webhook configurations only.

  - `item.config.kind` (string, required)
    Configuration type discriminator.
    Enum: "telegram", "webhook"

  - `item.config.bot_token` (string, required)
    Masked Telegram bot token returned in responses. The `bot_id` prefix is
preserved for identification; the secret `auth_token` portion is hidden,
showing only its last 4 characters: `{bot_id}:••••<last4>`.
To keep the stored token on update, omit `bot_token` from the request.
To rotate it, send a new plaintext token (see `TelegramBotToken`).

  - `item.config.chat_id` (integer, required)
    Telegram chat ID where notifications are sent.
Positive for private chats, negative for groups, `-100...` prefix for channels.
Use the Telegram Bot API `getUpdates` method to find your chat ID.

  - `item.config.language` (string, required)
    Language code for localization.
    Enum: "ru", "en"

  - `item.config.thread_id` (integer, required)
    Unique identifier for the target message thread (topic) of a forum.

Used for forum supergroups and private chats of bots with forum topic mode enabled only.
Default value is 0 (no specific thread).
    Example: 0

  - `item.config.url` (string, required)
    HTTP/HTTPS endpoint that accepts POST requests with JSON payloads.
Use headers (not query params) for authentication.

  - `item.config.headers` (object)
    Optional HTTP headers to include with webhook requests, including
receiver authentication headers such as `X-Webhook-Secret`.
Anyone authorized to read the channel can read these values; they are
not masked. The webhook signing secret is managed separately and
produces the `X-Webhook-Signature` header. Custom headers cannot override
signing or event headers. Other names starting with `X-Webhook-` are
reserved; header names are case-insensitive.
On update, omit `headers` to keep the stored set, send an object to
replace it, or `{}` to clear them.
    Example: {"X-Tenant-Id":"acme"}

  - `item.config.payload_version` (string, required)
    Webhook body contract a channel sends. It names the complete payload shape, not the HTTP route
and not the signature algorithm. Webhook channels created through v2 state it explicitly.
    Enum: "v1", "v2"

  - `item.created_at` (string, required)
    Timestamp when the resource was created.
    Example: 2024-01-15T10:30:00Z

  - `item.updated_at` (string, required)
    Timestamp when the resource was last updated.
    Example: 2024-01-15T10:30:00Z

  - `webhook_secret` (string)
    The full webhook signing secret, 55 characters: `vilna_whsec_{body}`
where `{body}` is 43 ASCII base62 characters. Used as the HMAC-SHA256 key
for the `X-Webhook-Signature` header on outgoing webhook deliveries.
**Returned only at channel creation and on rotation; store it
securely.** Lost secrets must be rotated.
    Example: vilna_whsec_a3K9pQ2mB5N8rT1xY7cZ4dH6eJ0fLm0123456789ABC

## Response 400:

  - `400` (unknown)
    Bad request (e.g., missing or invalid fields). Invalid text may be reported as address.label_invalid, address.meta_invalid, public_key.label_invalid, invoice.comment_invalid, invoice.metadata_invalid.

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

  - `type` (string, required)
    A URI that identifies the error type.
    Example: https://docs.vilna.io/apis/errors#internal-error

  - `title` (string, required)
    A short summary of the error type.
Use `detail` for information specific to this occurrence.
    Example: Bad 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: The address is already assigned to another invoice

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.
    Example: /traces/0123456789abcdef0123456789abcdef

  - `code` (string, required)
    Stable machine-readable error code 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

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

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

  - `fields.reason` (string, required)
    Why this field is invalid.
    Example: 32

## Response 401:

  - `401` (unknown)
    Missing or invalid authentication credentials.

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

  - `type` (string, required)
    A URI that identifies the error type.
    Example: https://docs.vilna.io/apis/errors#internal-error

  - `title` (string, required)
    A short summary of the error type.
Use `detail` for information specific to this occurrence.
    Example: Bad 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: The address is already assigned to another invoice

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.
    Example: /traces/0123456789abcdef0123456789abcdef

  - `code` (string, required)
    Stable machine-readable error code 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 403:

  - `403` (unknown)
    Insufficient permissions.

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

  - `type` (string, required)
    A URI that identifies the error type.
    Example: https://docs.vilna.io/apis/errors#internal-error

  - `title` (string, required)
    A short summary of the error type.
Use `detail` for information specific to this occurrence.
    Example: Bad 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: The address is already assigned to another invoice

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.
    Example: /traces/0123456789abcdef0123456789abcdef

  - `code` (string, required)
    Stable machine-readable error code 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 409:

  - `409` (unknown)
    Request conflicts with the current resource state.

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

  - `type` (string, required)
    A URI that identifies the error type.
    Example: https://docs.vilna.io/apis/errors#internal-error

  - `title` (string, required)
    A short summary of the error type.
Use `detail` for information specific to this occurrence.
    Example: Bad 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: The address is already assigned to another invoice

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.
    Example: /traces/0123456789abcdef0123456789abcdef

  - `code` (string, required)
    Stable machine-readable error code 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 429:

  - `429` (unknown)
    Resource quota exceeded. The response includes the effective limit for this scope. The applicable code and limit depend on the operation; examples illustrate different limits.

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

  - `type` (string, required)
    A URI that identifies the error type.
    Example: https://docs.vilna.io/apis/errors#internal-error

  - `title` (string, required)
    A short summary of the error type.
Use `detail` for information specific to this occurrence.
    Example: Bad 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: The address is already assigned to another invoice

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.
    Example: /traces/0123456789abcdef0123456789abcdef

  - `code` (string, required)
    Stable machine-readable code identifying the exhausted resource quota. The code is always
supplied by the service; clients branch on it instead of the human-readable detail.
    Example: address.quota_exceeded

  - `max` (integer, required)
    Effective maximum for this resource in the caller's scope. Zero prohibits new admissions;
it does not disable resources already held. This is the configured limit, not current usage.
    Example: 100

## Response 503:

  - `503` (unknown)
    The operation is temporarily unavailable. Please try again later.

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

  - `type` (string, required)
    A URI that identifies the error type.
    Example: https://docs.vilna.io/apis/errors#internal-error

  - `title` (string, required)
    A short summary of the error type.
Use `detail` for information specific to this occurrence.
    Example: Bad 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: The address is already assigned to another invoice

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.
    Example: /traces/0123456789abcdef0123456789abcdef

  - `code` (string, required)
    Stable machine-readable error code 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 default:

  - `default` (unknown)
    Unexpected error.

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

  - `type` (string, required)
    A URI that identifies the error type.
    Example: https://docs.vilna.io/apis/errors#internal-error

  - `title` (string, required)
    A short summary of the error type.
Use `detail` for information specific to this occurrence.
    Example: Bad 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: The address is already assigned to another invoice

  - `instance` (string)
    A URI that identifies this specific error occurrence.
Include this value when contacting support.
    Example: /traces/0123456789abcdef0123456789abcdef

  - `code` (string, required)
    Stable machine-readable error code 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 400 examples:

  - `The request body contains malformed JSON` (unknown)

## Response 401 examples:

  - `The API service refused authentication` (unknown)

## Response 403 examples:

  - `The API key lacks the required permission` (unknown)

## Response 409 examples:

  - `The address already has an active invoice` (unknown)

  - `The address cannot be deleted while in use` (unknown)

  - `A v1-created subscription rejects trigger, filter or noise changes` (unknown)

  - `The notification already has a manual attempt queued or in flight` (unknown)

  - `Retention has cleared the content, so there is nothing to resend` (unknown)

  - `The notification is a channel test` (unknown)

## Response 429 examples:

  - `The project has reached its address quota` (unknown)

  - `A zero effective limit prohibits new resources` (unknown)

## Response 503 examples:

  - `The operation cannot be completed at present` (unknown)

