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

# Create webhook

> Register an HTTPS endpoint to receive real-time event notifications

<blockquote className="agent-llms-directive">
  For the complete documentation index, see <a href="/llms.txt">llms.txt</a>.
</blockquote>

<Callout icon="circle-check" color="#16a34a">
  **Free** - does not consume credits
</Callout>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://xquik.com/api/v1/webhooks \
    -H "x-api-key: xq_YOUR_KEY_HERE" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://your-server.com/webhook",
      "eventTypes": ["tweet.new", "tweet.reply"]
    }' | jq
  ```

  ```javascript Node.js theme={null}
  const response = await fetch("https://xquik.com/api/v1/webhooks", {
    method: "POST",
    headers: {
      "x-api-key": "xq_YOUR_KEY_HERE",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://your-server.com/webhook",
      eventTypes: ["tweet.new", "tweet.reply"],
    }),
  });
  const webhook = await response.json();
  const webhookSecret = webhook.secret;
  // Store webhookSecret in your secret manager; do not print it in logs.
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://xquik.com/api/v1/webhooks",
      headers={"x-api-key": "xq_YOUR_KEY_HERE"},
      json={
          "url": "https://your-server.com/webhook",
          "eventTypes": ["tweet.new", "tweet.reply"],
      },
  )
  webhook = response.json()
  webhook_secret = webhook["secret"]
  # Store webhook_secret in your secret manager; do not print it in logs.
  ```

  ```go Go theme={null}
  package main

  import (
  	"bytes"
  	"encoding/json"
  	"log"
  	"net/http"
  )

  func main() {
  	payload := map[string]interface{}{
  		"url":        "https://your-server.com/webhook",
  		"eventTypes": []string{"tweet.new", "tweet.reply"},
  	}
  	body, err := json.Marshal(payload)
  	if err != nil {
  		log.Fatal(err)
  	}

  	req, err := http.NewRequest("POST", "https://xquik.com/api/v1/webhooks", bytes.NewReader(body))
  	if err != nil {
  		log.Fatal(err)
  	}
  	req.Header.Set("x-api-key", "xq_YOUR_KEY_HERE")
  	req.Header.Set("Content-Type", "application/json")

  	resp, err := http.DefaultClient.Do(req)
  	if err != nil {
  		log.Fatal(err)
  	}
  	defer resp.Body.Close()

    var webhook map[string]any
    if err := json.NewDecoder(resp.Body).Decode(&webhook); err != nil {
  		log.Fatal(err)
  	}
    secret, _ := webhook["secret"].(string)
    // Store secret in your secret manager; do not print it in logs.
    _ = secret
  }
  ```
</CodeGroup>

## Headers

<ParamField header="x-api-key" type="string" required>
  Your API key. Session cookie authentication is also supported.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`.
</ParamField>

## Body

<ParamField body="url" type="string" required>
  HTTPS endpoint URL where events will be delivered. HTTP URLs are rejected. URLs resolving to private or internal IP addresses (localhost, 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 169.254.x.x) are also rejected.
</ParamField>

<ParamField body="eventTypes" type="string[]" required>
  Array of event types to subscribe to. At least 1 required. Use any valid
  account monitor event type listed below. Keyword monitors emit only `tweet.*`
  event types. Account monitors can emit both `tweet.*` and `profile.*` event
  types.
</ParamField>

## Valid event types

Valid types: `tweet.new`, `tweet.quote`, `tweet.reply`, `tweet.retweet`,
`tweet.media`, `tweet.link`, `tweet.poll`, `tweet.mention`, `tweet.hashtag`,
`tweet.longform`, `profile.avatar.changed`, `profile.banner.changed`,
`profile.name.changed`, `profile.username.changed`, `profile.bio.changed`,
`profile.location.changed`, `profile.url.changed`, `profile.verified.changed`,
`profile.protected.changed`, `profile.pinned_tweet.changed`,
`profile.unavailable.changed`.

<CardGroup cols={2}>
  <Card title="tweet.new" icon="bell">
    Original tweet from an account monitor or matching keyword monitor. Use for
    new posts that are not replies, quotes, or retweets.
  </Card>

  <Card title="tweet.quote" icon="quote">
    Quote tweet from an account monitor or matching keyword monitor. Pair with
    monitors that include `tweet.quote`.
  </Card>

  <Card title="tweet.reply" icon="message-circle">
    Reply from an account monitor or matching keyword monitor. Pair with
    monitors that include `tweet.reply`.
  </Card>

  <Card title="tweet.retweet" icon="repeat-2">
    Retweet from an account monitor or matching keyword monitor. Pair with
    monitors that include `tweet.retweet`.
  </Card>
</CardGroup>

<Note>
  `webhook.test` is sent only by the [Test Webhook](/api-reference/webhooks/test) endpoint. It is not a subscribable `eventTypes` value.
</Note>

## Integration handoff

Use this endpoint after creating an account monitor with [`POST /monitors`](/api-reference/monitors/create) or a keyword monitor with [`POST /monitors/keywords`](/api-reference/monitors/create-keyword). The webhook stores the HTTPS endpoint and event-type filter. Active monitors produce the events; webhook delivery is included with monitor billing.

Store these fields immediately after creation:

<CardGroup cols={2}>
  <Card title="Webhook ID" icon="fingerprint">
    Store `id` for `POST /webhooks/{id}/test`, updates, deletes, and delivery
    lookups.
  </Card>

  <Card title="Delivery URL" icon="link">
    Store `url` to audit which queue, CRM, warehouse, or app endpoint receives
    monitor events.
  </Card>

  <Card title="Event Filter" icon="funnel">
    Store `eventTypes`; keep the webhook filter aligned with the account or
    keyword monitor event types.
  </Card>

  <Card title="Signing Secret" icon="shield-check">
    Store `secret` once and use it to verify `X-Xquik-Signature` on the raw
    request body.
  </Card>

  <Card title="Created At" icon="calendar-clock">
    Store `createdAt` for audit logs and configuration drift checks.
  </Card>

  <Card title="Delivery Payload" icon="webhook">
    Expect HTTPS `POST` bodies with `eventType`, `schemaVersion`, `deliveryId`,
    `streamEventId`, `occurredAt`, `data`, plus `username` for account monitor
    events or `query` for keyword monitor events.
  </Card>
</CardGroup>

Every delivery includes `X-Xquik-Signature`, `X-Xquik-Timestamp`, and
`X-Xquik-Nonce` headers. Use `deliveryId` as the per-endpoint idempotency key
and `streamEventId` when one monitor event must be processed once across retries
or endpoints.

Test the endpoint with [`POST /webhooks/{id}/test`](/api-reference/webhooks/test) before routing production events. Return a `2xx` response within 10 seconds, then process slow Slack, CRM, warehouse, or queue work asynchronously. Use [Signature Verification](/webhooks/verification) to validate the raw request body before processing.

## Response

<Tabs>
  <Tab title="201 Created">
    <ResponseField name="id" type="string">
      Unique webhook identifier.
    </ResponseField>

    <ResponseField name="url" type="string">
      The registered delivery endpoint.
    </ResponseField>

    <ResponseField name="eventTypes" type="string[]">
      Event types this webhook is subscribed to.
    </ResponseField>

    <ResponseField name="secret" type="string">
      HMAC signing secret (64-character hex string). Store securely. Returned only at creation time.
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      ISO 8601 creation timestamp.
    </ResponseField>

    ```json theme={null}
    {
      "id": "15",
      "url": "https://your-server.com/webhook",
      "eventTypes": ["tweet.new", "tweet.reply"],
      "secret": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
      "createdAt": "2026-02-24T10:30:00.000Z"
    }
    ```

    <Warning>
      The `secret` is returned **only once**. Store it securely. You need it to [verify webhook signatures](/webhooks/verification). It cannot be retrieved again.
    </Warning>
  </Tab>

  <Tab title="401 Unauthenticated">
    ```json theme={null}
    { "error": "unauthenticated", "message": "Missing or invalid API key" }
    ```

    Missing or invalid API key / session cookie.
  </Tab>

  <Tab title="400 Invalid Input">
    ```json theme={null}
    { "error": "invalid_input", "message": "Invalid URL or event types" }
    ```

    Invalid URL (must be HTTPS) or empty `eventTypes` array.
  </Tab>

  <Tab title="429 Rate Limited">
    ```json theme={null}
    { "error": "rate_limit_exceeded", "message": "Too many requests. Try again later.", "retryAfter": 60 }
    ```

    Too many requests. Wait for the `Retry-After` header before retrying.
  </Tab>
</Tabs>

<Note>
  This endpoint supports **dual authentication**: API key (`x-api-key` header) or session cookie from the dashboard.

  **Next steps:** [List Webhooks](/api-reference/webhooks/list) · [Signature Verification](/webhooks/verification) · [Webhooks Overview](/webhooks/overview)
</Note>
