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

# Subscribe to Webhooks

> Create a webhook subscription to receive real-time event notifications

Register a URL to receive POST requests whenever events happen on your job board. Supports generic webhooks, Discord, and Slack.

## Request Body

<ParamField body="event" type="string" required>
  The event to subscribe to. Use `*` to receive all events.

  Available events:

  * `job.created` — a new job is posted
  * `job.updated` — a job is edited
  * `job.approved` — a job is approved / set to active
  * `job.expired` — a job expires
  * `job.deleted` — a job is deleted
  * `job.status_changed` — any status transition
  * `application.received` — a new application is submitted
  * `application.status_changed` — an application status changes
  * `user.created` — a new job board user is created
  * `subscription.created` — a subscription starts
  * `subscription.canceled` — a subscription ends
  * `payment.received` — a payment succeeds
  * `*` — all events
</ParamField>

<ParamField body="target_type" type="string" required>
  The type of endpoint: `webhook`, `discord`, or `slack`.
</ParamField>

<ParamField body="target_url" type="string" required>
  The URL that will receive POST requests.
</ParamField>

<ParamField body="secret" type="string">
  An HMAC signing secret (min 16 characters). When provided, every delivery includes an `X-Kardow-Signature` header so you can verify authenticity. Only used for `webhook` target type.
</ParamField>

## Webhook Payload

Every delivery sends a JSON body like this:

```json theme={null}
{
  "event": "job.created",
  "organization_id": 42,
  "timestamp": "2025-07-19T12:00:00.000Z",
  "data": {
    "job_id": "abc-123",
    "title": "Senior Engineer",
    "company_name": "Acme Inc",
    "status": "active",
    "location": "Remote"
  }
}
```

Discord and Slack targets automatically format the payload into embeds / blocks.

## Signature Verification

If you provided a `secret`, verify deliveries like this:

```javascript theme={null}
const crypto = require('crypto');

function verifySignature(body, secret, signature) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}
```

## Examples

### Subscribe to all job events via webhook

```bash cURL theme={null}
curl --request POST \
  --url "https://api.kardow.com/webhooks" \
  --header "x-api-key: your-api-key-here" \
  --header "Content-Type: application/json" \
  --data '{
    "event": "job.created",
    "target_type": "webhook",
    "target_url": "https://example.com/my-webhook",
    "secret": "my-signing-secret-1234"
  }'
```

### Subscribe a Discord channel

```bash cURL theme={null}
curl --request POST \
  --url "https://api.kardow.com/webhooks" \
  --header "x-api-key: your-api-key-here" \
  --header "Content-Type: application/json" \
  --data '{
    "event": "*",
    "target_type": "discord",
    "target_url": "https://discord.com/api/webhooks/123/abc"
  }'
```

## Response

<ResponseField name="data" type="object">
  <Expandable title="properties">
    <ResponseField name="id" type="string">Subscription ID</ResponseField>
    <ResponseField name="event" type="string">Event name</ResponseField>
    <ResponseField name="target_type" type="string">Target type</ResponseField>
    <ResponseField name="target_url" type="string">Target URL</ResponseField>
    <ResponseField name="is_enabled" type="boolean">Whether the subscription is active</ResponseField>
    <ResponseField name="created_at" type="string">ISO 8601 timestamp</ResponseField>
  </Expandable>
</ResponseField>


## OpenAPI

````yaml POST /webhooks
openapi: 3.1.0
info:
  title: Kardow Public API
  description: Public API for jobs and member automation on Kardow.
  version: 1.0.0
  contact:
    name: Kardow Support
    email: support@kardow.com
    url: https://kardow.com
servers:
  - url: https://api.kardow.com
    description: Production API
security:
  - ApiKeyAuth: []
paths:
  /webhooks:
    post:
      tags:
        - Webhooks
      summary: Subscribe to Webhooks
      description: >-
        Create a webhook subscription for an event or return the existing
        matching subscription.
      operationId: createWebhookSubscription
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateWebhookSubscriptionRequest'
            examples:
              all_events:
                summary: Subscribe to all events
                value:
                  event: '*'
                  target_type: webhook
                  target_url: https://example.com/kardow/webhook
                  secret: replace-with-a-16-char-secret
              zapier_job_created:
                summary: Subscribe a Zapier hook to job.created
                value:
                  event: job.created
                  target_type: webhook
                  target_url: https://hooks.zapier.com/hooks/catch/123456/abcdef/
      responses:
        '200':
          description: Existing subscription returned
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSubscriptionResponse'
        '201':
          description: Subscription created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookSubscriptionResponse'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '500':
          $ref: '#/components/responses/InternalError'
      security:
        - ApiKeyAuth: []
components:
  schemas:
    CreateWebhookSubscriptionRequest:
      type: object
      required:
        - event
        - target_type
        - target_url
      properties:
        event:
          type: string
          enum:
            - job.created
            - job.updated
            - job.approved
            - job.expired
            - job.deleted
            - job.status_changed
            - application.received
            - application.status_changed
            - user.created
            - subscription.created
            - subscription.canceled
            - payment.received
            - '*'
        target_type:
          type: string
          enum:
            - webhook
            - discord
            - slack
        target_url:
          type: string
          format: uri
        secret:
          type: string
          minLength: 16
      additionalProperties: false
    WebhookSubscriptionResponse:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/WebhookSubscription'
      required:
        - data
    WebhookSubscription:
      type: object
      properties:
        id:
          type: string
          format: uuid
        event:
          type: string
          enum:
            - job.created
            - job.updated
            - job.approved
            - job.expired
            - job.deleted
            - job.status_changed
            - application.received
            - application.status_changed
            - user.created
            - subscription.created
            - subscription.canceled
            - payment.received
            - '*'
        target_type:
          type: string
          enum:
            - webhook
            - discord
            - slack
        target_url:
          type: string
          format: uri
        is_enabled:
          type: boolean
        created_at:
          type:
            - string
            - 'null'
          format: date-time
        updated_at:
          type:
            - string
            - 'null'
          format: date-time
      required:
        - id
        - event
        - target_type
        - target_url
        - is_enabled
    ErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            message:
              type: string
            code:
              type: string
            details: {}
            url:
              type: string
          required:
            - message
            - code
      required:
        - error
  responses:
    ValidationError:
      description: Validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    UnauthorizedError:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        API key for authentication. Get yours from Settings > API Keys in the
        Kardow dashboard.

````