---
title: "Create Webhook Subscription"
method: POST
path: "/v1/webhooks"
tags: ["Webhooks"]
---

# Create Webhook Subscription

`POST /v1/webhooks`

The provided HTTPS endpoint will receive HTTP POST requests with a signed JSON envelope whenever subscribed events occur.

## Webhook Envelope Format

Each webhook delivery contains a JSON envelope with the following structure:

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "CASHOUT.PIX.TRANSFERS.COMPLETED",
  "version": "1.0",
  "data": { ... },
  "created_at": "2026-03-10T12:00:00Z"
}
```

- `id`: Event identifier (UUID). **Important**: This ID changes on every retry and must NOT be used for deduplication.
- `type`: Concrete event type (see Event Types below).
- `version`: Envelope schema version (currently "1.0").
- `data`: Event payload (structure depends on event type).
- `created_at`: Timestamp when the event was generated.

## Event Types by Subscription

### `CASHIN.PIX.QRCODES`
Triggers when dynamic PIX QR codes are created or paid:
- `CASHIN.PIX.QRCODES.CREATED` — QR code created via API
- `CASHIN.PIX.QRCODES.PAID` — QR code paid by external payer

### `CASHIN.DEPOSITS`
Triggers when incoming PIX transfers are received (excluding QR code payments):
- `CASHIN.DEPOSITS.RECEIVED` — Deposit received in the account

### `CASHOUT.PIX.TRANSFERS`
Triggers for PIX transfer lifecycle events:
- `CASHOUT.PIX.TRANSFERS.SCHEDULED` — Transfer scheduled for future execution
- `CASHOUT.PIX.TRANSFERS.COMPLETED` — Transfer completed successfully
- `CASHOUT.PIX.TRANSFERS.FAILED` — Transfer failed during processing
- `CASHOUT.PIX.TRANSFERS.SCHEDULED.FAILED` — Scheduled transfer failed during execution

Planned (not yet delivered): `CASHOUT.PIX.TRANSFERS.CREATED`

### `CASHOUT.PIX.REFUNDS`
Triggers for PIX refund events:
- `CASHOUT.PIX.REFUNDS.COMPLETED` — Refund completed successfully
- `CASHOUT.PIX.REFUNDS.FAILED` — Refund failed during processing

### `CASHOUT.BOLETO.PAYMENTS`
Triggers for boleto payment lifecycle events:
- `CASHOUT.BOLETO.PAYMENTS.SCHEDULED` — Boleto payment scheduled for future execution
- `CASHOUT.BOLETO.PAYMENTS.COMPLETED` — Boleto payment completed successfully
- `CASHOUT.BOLETO.PAYMENTS.FAILED` — Boleto payment failed (authorization, void, or processing error)
- `CASHOUT.BOLETO.PAYMENTS.SCHEDULED.FAILED` — Scheduled boleto payment failed during execution

### `CASHOUT.PIX.QRCODE.PAYMENTS`
Reserved for future PIX QR code payment events. No webhook delivery yet.

## Signature Verification

All webhook deliveries include cryptographic signatures in HTTP headers:
- `Content-Type`: `application/json`
- `x-kiwify-digital-signature`: Base64url-encoded EdDSA-Ed25519 signature (no padding)
- `x-kiwify-timestamp`: Unix timestamp in milliseconds

Fetch public keys from `GET /v1/webhooks-keys` to verify webhook authenticity.
Reconstruct the signed message as `{path}:POST:{raw_body}:{timestamp}`, SHA-256 hash it, then verify with Ed25519.

## Idempotency

**Important**: The envelope `id` is an event identifier that changes on every retry attempt.
Do NOT use it for deduplication.

Instead, deduplicate using the resource identifiers inside `data`:
- For transfers: `transfer_id` + event `type`
- For QR codes: `qrcode_id` + event `type`
- For deposits: `transaction_id` + event `type`
- For refunds: `refund_id` + event `type`

Example: If a transfer webhook is retried, you'll receive two different envelope `id` values
but the same `transfer_id` in the `data` payload.

## Rules
- The endpoint URL must use HTTPS.
- A bank account can have at most 10 active webhook subscriptions.
- Each (bank_account, endpoint_url) combination must be unique among active subscriptions.
- At least one event type must be specified.

## Request body

- CreateWebhookRequest — Request body for creating a webhook subscription.
  - `event_types` WebhookEventTypeDoc[], required — List of event types to subscribe to. At least one is required.
  - `url` string, required — HTTPS endpoint URL that will receive webhook event notifications. Must be a valid HTTPS URL with a maximum length of 2048 characters.

## Response `201`

Webhook subscription created successfully

- CreateWebhookResponse — Response returned after successfully creating a webhook subscription.
  - `message` string, required — Human-readable confirmation message.
  - `webhook` WebhookDto, required — Webhook subscription representation.
    - `id` string, required — Unique identifier of the webhook subscription.
    - `subscriptions` WebhookEventTypeDoc[], required — List of event types this webhook is subscribed to.
    - `url` string, required — The endpoint URL registered to receive webhook events.

## Other responses

- `400` — Invalid request: malformed JSON, wrong Content-Type, missing required fields, non-HTTPS URL, or invalid event_types. Error codes: WRONG_CONTENT_TYPE, MALFORMED_JSON, INVALID_JSON_BODY, VALIDATION_ERROR
- `401` — Authentication failed - invalid or missing service account credentials
- `403` — Access denied - service account requires ManageWebhooks permission
- `409` — Conflict: a subscription with this URL already exists for this account. Error code: DUPLICATE_ENDPOINT
- `422` — Unprocessable entity: the account has reached the maximum of 10 active webhook subscriptions. Error code: MAX_SUBSCRIPTIONS_REACHED
- `500` — Internal server error. Error code: INTERNAL_ERROR

---

[API](https://skmtc.net/kiwify/apis/conta-digital-api.md) · [All operations](https://skmtc.net/kiwify/apis/conta-digital-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/kiwify/conta-digital-api/revisions/5e009b25c245/schema)
