> ## 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.

# Zapier Twitter automation with webhooks & X API

> Build Zapier Twitter automation for tweet search, follower exports, monitors, signed webhooks, extraction jobs, and approved X actions with exact code.

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

Build Zapier Twitter automation through Xquik without an X Developer app. Search
tweets, export followers, route webhooks, and approve write actions.

Build a private Zapier Platform CLI integration.
Add routes after Zap history confirms repeated demand.

## Replace the retired Zapier Twitter integration

Zapier retired its native Twitter integration in 2023. Xquik restores those
workflows through an API-key connection.

Choose an Xquik connection based on the workflow:

<CardGroup cols={2}>
  <Card title="API by Zapier" icon="braces">
    Use one reusable API-key connection for a small number of actions. Keep the
    Xquik key in the app connection instead of a Zap field.
  </Card>

  <Card title="Private platform integration" icon="blocks">
    Use the CLI for shared actions, polling triggers, and REST Hooks. Add
    samples, output fields, custom errors, and shared field mapping.
  </Card>

  <Card title="Webhooks by Zapier" icon="webhook">
    Use simple unauthenticated payloads only. Zapier keeps credentials in
    Webhooks step fields. People with Zap access can read them.
  </Card>

  <Card title="Search versus monitor" icon="scan-search">
    Search existing tweets with an action. Monitor new tweets, replies, quotes,
    or reposts with polling or signed webhook events.
  </Card>
</CardGroup>

This guide builds the private Platform integration.

## Prerequisites

* [Xquik API key](/x-api-quickstart)
* Zapier account with Platform CLI access
* A Node.js release supported by the current Zapier Platform CLI
* HTTPS callback URLs for REST Hook testing

Install and sign in to the Zapier CLI:

```bash theme={null}
npm install -g zapier-platform-cli
zapier-platform --version
zapier login
zapier init xquik-zapier --template minimal
cd xquik-zapier
npm install
```

## Integration shape

<CardGroup cols={2}>
  <Card title="Auth" icon="key-round">
    API key field named `apiKey`, injected as `x-api-key`.
  </Card>

  <Card title="Base URL" icon="link">
    `https://xquik.com/api/v1`
  </Card>

  <Card title="Request helper" icon="workflow">
    JSON requests, structured Xquik errors, and `Retry-After` handling.
  </Card>

  <Card title="Actions" icon="play">
    Search Tweets, Get User, Read Followers, Create Extraction, and Create
    Monitor. Add Create Webhook, Create Tweet, and Create Reply.
  </Card>

  <Card title="Triggers" icon="radio">
    New Matching Tweet polling and Monitor Event instant trigger. Add Extraction
    Completed and Webhook Delivery Failure polling.
  </Card>
</CardGroup>

## API key auth

Add a custom auth field and inject it into every Xquik request:

```javascript theme={null}
const BASE_URL = "https://xquik.com/api/v1";

function addApiKeyHeader(request, z, bundle) {
  request.headers = request.headers || {};
  request.headers["x-api-key"] = bundle.authData.apiKey;
  return request;
}

async function testAuth(z) {
  const response = await z.request({
    method: "GET",
    url: `${BASE_URL}/account`,
  });

  return response.data;
}

module.exports = {
  authentication: {
    type: "custom",
    fields: [{ key: "apiKey", label: "Xquik API Key", required: true }],
    test: testAuth,
  },
  beforeRequest: [addApiKeyHeader],
};
```

## Shared error handling

Normalize Xquik responses in one helper. Every action then reports the same remediation:

```javascript theme={null}
function throwForXquikError(z, response) {
  if (response.status < 400) {
    return;
  }

  const detail = response.data?.message || response.data?.error;

  if (response.status === 400) {
    throw new z.errors.Error(
      detail || "Invalid request. Fix the input fields.",
      "XquikInvalidRequest",
      400,
    );
  }

  if (response.status === 401) {
    throw new z.errors.RefreshAuthError("Authentication failed. Check the Xquik API key.");
  }

  if (response.status === 402) {
    throw new z.errors.Error(
      "Subscription or credits required. Update billing in Xquik.",
      "XquikBillingRequired",
      402,
    );
  }

  if (response.status === 404) {
    throw new z.errors.Error(
      detail || "Resource not found. Check the supplied ID.",
      "XquikResourceMissing",
      404,
    );
  }

  if (response.status === 424) {
    throw new z.errors.Error(
      detail || "X dependency failed. Retry only when the workflow is safe.",
      "XquikDependencyFailure",
      424,
    );
  }

  if (response.status === 429) {
    const retryAfter = Number.parseInt(
      response.getHeader?.("retry-after") || response.headers?.["retry-after"] || "",
      10,
    );

    if (Number.isInteger(retryAfter) && retryAfter > 0) {
      throw new z.errors.ThrottledError("Rate limited. Zapier scheduled a retry.", retryAfter);
    }

    throw new z.errors.Error(
      "Rate limited. Retry after the cooldown period.",
      "XquikRateLimit",
      429,
    );
  }

  if (response.status === 502) {
    throw new z.errors.Error(
      detail || "X retrieval failed. Retry with a bounded attempt count.",
      "XquikRetrievalFailure",
      502,
    );
  }

  throw new z.errors.Error(
    detail || "Xquik request failed. Inspect the safe status.",
    "XquikRequestError",
    response.status,
  );
}

async function xquikRequest(z, options) {
  const response = await z.request({
    ...options,
    skipThrowForStatus: true,
    throwForThrottlingEarly: false,
  });

  throwForXquikError(z, response);
  return response;
}
```

Call `xquikRequest(z, options)` before reading response data. Use each route's
response widget as the canonical status list.

## Starter actions

<CardGroup cols={2}>
  <Card title="Search tweets" icon="search">
    Call `GET /x/tweets/search` with `q`. Use `cursor` for page loops. Keep `limit` on bounded resumes.
  </Card>

  <Card title="Get tweet" icon="message-square">
    Call `GET /x/tweets/{id}` with a tweet ID.
  </Card>

  <Card title="Get user" icon="user">
    Call `GET /x/users/{id}` with a username or numeric user ID.
  </Card>

  <Card title="Read followers" icon="user-plus">
    Call `GET /x/users/{id}/followers`. Keep `next_cursor` between pages.
  </Card>

  <Card title="Read following" icon="user-check">
    Call `GET /x/users/{id}/following`. Keep following rows separate.
  </Card>

  <Card title="Read tweet replies" icon="messages-square">
    Call `GET /x/tweets/{id}/replies`. Keep parent and reply tweet IDs.
  </Card>

  <Card title="Get trends" icon="trending-up">
    Call `GET /x/trends` with optional `woeid` and `count`.
  </Card>

  <Card title="Create tweet" icon="send">
    Call `POST /x/tweets` with account, text, and optional public media URLs.
  </Card>

  <Card title="Create reply" icon="reply">
    Call `POST /x/tweets` with account, text, and `reply_to_tweet_id`.
  </Card>

  <Card title="Create extraction" icon="database">
    Call `POST /extractions` with `toolType`, query fields, and result limit.
  </Card>

  <Card title="Create monitor" icon="radio">
    Call `POST /monitors` with username and event types.
  </Card>

  <Card title="Create webhook" icon="webhook">
    Call `POST /webhooks` with callback URL and event types.
  </Card>
</CardGroup>

## Result handoff

Use Zapier samples and `outputFields` so later Zap steps map stable values.
Use snake\_case storage keys when direct API responses use camelCase. Return
compact objects from actions and arrays from triggers.

<CardGroup cols={2}>
  <Card title="Search tweets action" icon="search">
    Return `id`, `tweet_id`, `text`, `author_username`, `created_at`, and `url`. Keep `has_next_page` and `next_cursor` when a Zap loops pages.
  </Card>

  <Card title="User profile rows" icon="users">
    Return source `id` as `user_id`. Keep `username`, `name`, `followers`, `verified`, and `profile_picture`. Store the input, `has_next_page`, and `next_cursor` separately.
  </Card>

  <Card title="Follower and following rows" icon="user-check">
    Store `user_id`, `username`, `name`, `followers`, `following`, `verified`, and `profile_picture`. Keep the source account, page cursor, and relationship direction.
  </Card>

  <Card title="Trend rows" icon="trending-up">
    Return each trend `name`, `rank`, `query`, and `description`. Keep response `count`, `woeid`, and the selected region with the Zap run.
  </Card>

  <Card title="Tweet or reply write" icon="send">
    Send a unique `Idempotency-Key`. Store `id`, `status`, `billing`, `result`, and `statusUrl`. Poll while `terminal` is false. Retry only when `safeToRetry` is true, using a new key.
  </Card>

  <Card title="Media attachments" icon="image">
    For tweets or replies, pass public URLs in `media`. Do not send `media_ids`. For DMs, upload first. Pass 1 `media_id` in `media_ids`. Store `message_id` and leave `reply_to_message_id` unset.
  </Card>

  <Card title="Monitor and webhook setup" icon="radio">
    Return monitor `id`, `username`, `xUserId`, `eventTypes`, `isActive`, and `nextBillingAt`. Return webhook `id`, `url`, `eventTypes`, and one-time `secret`. Map production `deliveryId` to `delivery_id` for receiver retry deduplication. Map `streamEventId` to `stream_event_id` for event deduplication across webhook changes.
  </Card>

  <Card title="REST hook trigger" icon="fingerprint">
    Return `id`, `delivery_id`, and `stream_event_id`. Use delivery IDs for endpoint retries. Use event IDs across webhook changes.
  </Card>

  <Card title="Stored event replay" icon="activity">
    Call `GET /events` with `cursor` when a Zap needs replay. Map event and monitor IDs first. Then map `occurredAt`, `hasMore`, and `nextCursor` to snake\_case fields.
  </Card>

  <Card title="Receiver acceptance" icon="copy-check">
    Zapier controls responses from its generated hook URL. Use a relay when you
    need custom acceptance or replay rules. Keep signing values, raw bodies, and
    raw signatures outside Zap history. Keep full headers outside business apps
    too.
  </Card>

  <Card title="Extraction polling trigger" icon="database">
    Return completed job `id`, `toolType`, and `status`. Store normalized IDs, status, `has_more`, and `next_cursor`. Then fetch detail rows.
  </Card>
</CardGroup>

Map friendly region names to WOEID values. Submit the selected value to Xquik.

Example search action:

```javascript theme={null}
async function performSearchTweets(z, bundle) {
  const response = await xquikRequest(z, {
    method: "GET",
    url: `${BASE_URL}/x/tweets/search`,
    params: {
      q: bundle.inputData.q,
      queryType: bundle.inputData.queryType || "Latest",
      limit: bundle.inputData.limit || 25,
      cursor: bundle.inputData.cursor || undefined,
    },
  });

  const page = response.data;

  return (page.tweets || []).map((tweet) => ({
    id: tweet.id,
    tweet_id: tweet.id,
    text: tweet.text,
    author_username: tweet.author?.username || null,
    created_at: tweet.createdAt || null,
    url: tweet.url || null,
    has_next_page: Boolean(page.has_next_page),
    next_cursor: page.next_cursor || null,
  }));
}

module.exports = {
  key: "search_tweets",
  noun: "Tweet",
  display: {
    label: "Search Tweets",
    description: "Find recent tweets that match a search query.",
  },
  operation: {
    inputFields: [
      { key: "q", label: "Query", required: true, type: "string" },
      {
        key: "queryType",
        label: "Order",
        required: false,
        choices: ["Latest", "Top"],
      },
      { key: "limit", label: "Limit", required: false, type: "integer" },
      { key: "cursor", label: "Cursor", required: false, type: "string" },
    ],
    perform: performSearchTweets,
    sample: {
      id: "1840000000000000000",
      tweet_id: "1840000000000000000",
      text: "Example tweet text",
      author_username: "example_user",
      created_at: "2026-08-02T12:00:00Z",
      url: "https://x.com/example_user/status/1840000000000000000",
      has_next_page: true,
      next_cursor: "DAACCgACExample",
    },
    outputFields: [
      { key: "id", label: "Tweet ID" },
      { key: "tweet_id", label: "Tweet ID for Storage" },
      { key: "text", label: "Text" },
      { key: "author_username", label: "Author Username" },
      { key: "created_at", label: "Created At" },
      { key: "url", label: "URL" },
      { key: "has_next_page", label: "Has Next Page", type: "boolean" },
      { key: "next_cursor", label: "Next Cursor" },
    ],
  },
};
```

## Trigger 1: new matching tweet

Poll searches for query alerts. Return stable tweet records with unique IDs.

```javascript theme={null}
async function performNewMatchingTweet(z, bundle) {
  return performSearchTweets(z, {
    ...bundle,
    inputData: {
      ...bundle.inputData,
      cursor: undefined,
      limit: 100,
      queryType: "Latest",
    },
  });
}
```

Return newest tweets first. Never reuse saved cursors across polling runs.

## Trigger 2: monitor event REST hook

Use REST Hooks for near-real-time tweets, replies, quotes, and retweets.

### Choose a secure webhook path

Use a verification relay. Keep the one-time signing secret outside Zapier logs.

<CardGroup cols={2}>
  <Card title="Production relay" icon="shield-check">
    Let the relay create the Xquik webhook. Store its secret only in the relay.
    Verify HMAC, timestamp, nonce, delivery ID, and event ID there. Forward a
    normalized event to `bundle.targetUrl` after verification.
  </Card>

  <Card title="Direct private prototype" icon="flask-conical">
    Use direct Zapier subscription only for isolated testing. The returned
    secret enters `bundle.subscribeData` and may enter Zapier HTTP logs. Never
    use this path when that exposure violates your secret policy.
  </Card>
</CardGroup>

### Direct prototype subscribe

```javascript theme={null}
async function subscribeHook(z, bundle) {
  const response = await xquikRequest(z, {
    method: "POST",
    url: `${BASE_URL}/webhooks`,
    body: {
      url: bundle.targetUrl,
      eventTypes: bundle.inputData.eventTypes,
    },
  });

  return {
    id: response.data.id,
    secret: response.data.secret,
    eventTypes: response.data.eventTypes,
  };
}
```

Zapier stores this result in `bundle.subscribeData`. Never expose the `secret`
through trigger output. Use this path only for isolated testing.

### Unsubscribe

```javascript theme={null}
async function unsubscribeHook(z, bundle) {
  const response = await xquikRequest(z, {
    method: "DELETE",
    url: `${BASE_URL}/webhooks/${bundle.subscribeData.id}`,
  });

  return response.data;
}
```

### Direct prototype perform

```javascript theme={null}
const { createHmac, timingSafeEqual } = require("node:crypto");

const FIVE_MINUTES_MS = 5 * 60 * 1000;

function readRawHeader(headers, requestedName) {
  const wanted = requestedName.toLowerCase();

  for (const [name, value] of Object.entries(headers || {})) {
    const normalized = name.toLowerCase().replace(/^http-/, "");
    if (normalized === wanted) {
      return String(value);
    }
  }

  return "";
}

function verifyXquikWebhook(z, bundle) {
  const rawBody = bundle.rawRequest?.content;
  const headers = bundle.rawRequest?.headers;
  const secret = bundle.subscribeData?.secret;
  const timestamp = readRawHeader(headers, "x-xquik-timestamp");
  const nonce = readRawHeader(headers, "x-xquik-nonce");
  const signature = readRawHeader(headers, "x-xquik-signature");

  if (!rawBody || !secret || !timestamp || !nonce || !signature) {
    throw new z.errors.Error(
      "Webhook rejected. Signing fields are missing.",
      "XquikWebhookVerificationError",
      400,
    );
  }

  const timestampMs = Number(timestamp);
  if (!Number.isFinite(timestampMs) || Math.abs(Date.now() - timestampMs) > FIVE_MINUTES_MS) {
    throw new z.errors.Error(
      "Webhook rejected. Timestamp is stale or invalid.",
      "XquikWebhookVerificationError",
      400,
    );
  }

  const signingString = `${timestamp}.${nonce}.${rawBody}`;
  const expected = "sha256=" + createHmac("sha256", secret).update(signingString).digest("hex");
  const expectedBuffer = Buffer.from(expected);
  const actualBuffer = Buffer.from(signature);

  if (
    expectedBuffer.length !== actualBuffer.length ||
    !timingSafeEqual(expectedBuffer, actualBuffer)
  ) {
    throw new z.errors.Error(
      "Webhook rejected. Signature does not match.",
      "XquikWebhookVerificationError",
      400,
    );
  }

  return JSON.parse(rawBody);
}

function performMonitorEvent(z, bundle) {
  const payload = verifyXquikWebhook(z, bundle);

  return [
    {
      id: payload.streamEventId || payload.deliveryId || String(payload.timestamp),
      delivery_id: payload.deliveryId || null,
      stream_event_id: payload.streamEventId || null,
      event_type: payload.eventType,
      occurred_at: payload.occurredAt || payload.timestamp,
      username: payload.username || null,
      tweet_id: payload.data?.id || null,
      text: payload.data?.text || null,
      author_username: payload.data?.author?.userName || payload.username || null,
    },
  ];
}
```

Verify `bundle.rawRequest.content` before using `bundle.cleanedRequest`.
Zapier prefixes most inbound raw headers with `Http-`. Normalize that prefix.

Zapier does not provide a durable 5-minute nonce store.
Place a durable verification relay before Zapier in production.
Verify HMAC, timestamps, and nonces there.
Deduplicate `deliveryId` and `streamEventId` before forwarding.
Zapier owns responses from `bundle.targetUrl`.

```javascript theme={null}
async function performList(z) {
  const response = await xquikRequest(z, {
    method: "GET",
    url: `${BASE_URL}/events`,
    params: { limit: 1 },
  });

  return (response.data.events || []).slice(0, 1).map((event) => ({
    id: event.id,
    delivery_id: null,
    stream_event_id: event.id,
    event_type: event.type,
    occurred_at: event.occurredAt,
    username: event.username || null,
    tweet_id: event.data?.id || null,
    text: event.data?.text || null,
    author_username: event.data?.author?.userName || event.username || null,
  }));
}
```

Testing a REST Hook calls `performList`, not the live `perform` handler.

## Trigger 3: extraction completed

```javascript theme={null}
async function performCompletedExtractions(z) {
  const response = await xquikRequest(z, {
    method: "GET",
    url: `${BASE_URL}/extractions`,
    params: { status: "completed", limit: 25 },
  });

  return response.data.extractions || [];
}
```

## Trigger 4: webhook delivery failure

```javascript theme={null}
async function performWebhookFailures(z, bundle) {
  const response = await xquikRequest(z, {
    method: "GET",
    url: `${BASE_URL}/webhooks/${bundle.inputData.webhookId}/deliveries`,
  });

  return (response.data.deliveries || []).filter((delivery) => delivery.status === "failed");
}
```

## Build useful Zapier Twitter automation

<CardGroup cols={2}>
  <Card title="Zapier Twitter Google Sheets" icon="table-2">
    Search tweets with one saved query. Append IDs, text, authors, times, and
    URLs to Google Sheets. Resume with that query's `next_cursor`.
  </Card>

  <Card title="Zapier Twitter to Slack" icon="bell-ring">
    Verify each monitor event. Send normalized tweets, accounts, event types,
    and URLs to Slack in real time.
  </Card>

  <Card title="Zapier Twitter Discord" icon="message-circle">
    Send matched tweets or replies to Discord. Include the author, timestamp,
    tweet URL, and search query.
  </Card>

  <Card title="Zapier RSS to Twitter" icon="rss">
    Read blog posts from an RSS feed. Create approved scheduled tweets with the
    article title, URL, and campaign tags.
  </Card>

  <Card title="Zapier post to Twitter" icon="send">
    Review the Twitter account, text, reply target, and media. Then run the
    approved write action with an idempotency key.
  </Card>

  <Card title="Followers to CRM" icon="contact-round">
    Use an automated workflow for bounded follower pages. Choose an extraction
    job for durable CSV, JSON, or XLSX exports.
  </Card>
</CardGroup>

Zapier Twitter Google Sheets workflows store searchable tweet rows.
Zapier Twitter to Slack sends verified alerts in real time.
Zapier Twitter Discord sends matched tweet notifications.
Zapier RSS to Twitter converts blog posts from an RSS feed.

Review scheduled tweets and the Twitter account before scheduling posts.
Approve every Zapier post to Twitter step.
Keep each automated workflow focused.

Compare other Twitter automation tools before choosing.

## Test coverage

Add focused Zapier tests before sharing the private app:

<CardGroup cols={2}>
  <Card title="Auth header injection" icon="shield-check">
    Every request includes `x-api-key` from `bundle.authData.apiKey`.
  </Card>

  <Card title="Invalid key" icon="key-round">
    `401` returns "Authentication failed. Check the Xquik API key."
  </Card>

  <Card title="Invalid input" icon="circle-x">
    `400` returns the safe Xquik detail or an input-field remediation.
  </Card>

  <Card title="Billing required" icon="credit-card">
    `402` tells the user to update subscription or credits.
  </Card>

  <Card title="Missing resource" icon="search-x">
    `404` identifies a bad tweet, user, monitor, webhook, or extraction ID.
  </Card>

  <Card title="Dependency failure" icon="unplug">
    `424` stops unsafe automatic retries and keeps a safe explanation.
  </Card>

  <Card title="Rate limit" icon="timer">
    `429` creates `ThrottledError` with `Retry-After` when present.
  </Card>

  <Card title="Retrieval failure" icon="triangle-alert">
    `502` uses a bounded retry policy outside unsafe write actions.
  </Card>

  <Card title="REST hook subscribe" icon="webhook">
    The relay stores `bundle.targetUrl`, selected event types, and the secret.
  </Card>

  <Card title="REST hook unsubscribe" icon="radio">
    `DELETE /webhooks/{id}` uses `bundle.subscribeData.id`.
  </Card>

  <Card title="Sample output" icon="database">
    `performList` and live `perform` return identical snake\_case fields.
  </Card>

  <Card title="Search action" icon="search">
    Search returns stable tweet IDs, authors, timestamps, URLs, and cursors.
  </Card>

  <Card title="Raw-body signature" icon="shield-check">
    Changing one raw body byte makes HMAC verification fail.
  </Card>

  <Card title="Stale timestamp" icon="clock-alert">
    The relay rejects a timestamp outside 5 minutes before JSON parsing.
  </Card>

  <Card title="Secret hygiene" icon="vault">
    Production Zapier output and logs never receive the Xquik signing secret.
  </Card>

  <Card title="Relay replay store" icon="database-zap">
    The relay rejects reused nonces and deduplicates delivery and event IDs.
  </Card>

  <Card title="Acknowledgement ownership" icon="network">
    Relay tests cover custom `2xx` rules. Zapier owns its hook URL response.
  </Card>
</CardGroup>

Run the local Zapier suite with:

```bash theme={null}
zapier-platform test
zapier-platform validate
```

## Zapier Twitter automation questions

### How does a Zapier Twitter API connection work?

A private Platform app calls Xquik with one API key.

### Why does the native Zapier Twitter integration not work?

Zapier retired it in 2023. Use a private Platform integration.

### What does a Twitter Zapier workflow replace?

It replaces manual copying across tweet workflows.

### Can Zapier search tweets by keyword?

Yes. Search with `q`. Store tweet IDs and cursors.

### Can Zapier export Twitter followers?

Yes. Page small lists. Export larger ones.

### Should a zap poll or use a Twitter webhook?

Poll delayed searches. Use signed hooks for live monitors.

### How does Zapier verify an Xquik webhook?

Use a production relay. Verify HMAC over the raw body there.
Check the timestamp and nonce before parsing JSON. Reject reused nonces.
Deduplicate delivery and event IDs before forwarding events to Zapier.

### Can Zapier post tweets and replies?

Yes. Use Zapier to automate tweets only after explicit approval.

### Can Zapier schedule posts from an RSS feed?

Convert RSS items into scheduled tweets. Approve them before publishing.

## Zapier and Xquik sources

* [Zapier Platform CLI quickstart](https://docs.zapier.com/integrations/quickstart/build-integration)
* [Zapier Twitter integration removal](https://help.zapier.com/hc/en-us/articles/43554846358541-App-update-Twitter-integration-removal)
* [Zapier API request options](https://help.zapier.com/hc/en-us/articles/44391646192397-Ways-to-make-API-requests-in-Zapier)
* [Zapier REST Hook trigger](https://docs.zapier.com/integrations/build/cli-hook-trigger)
* [Zapier bundle reference](https://docs.zapier.com/integrations/build/bundle)
* [Zapier HTTP request logging](https://docs.zapier.com/integrations/build-cli/making-http-requests)
* [Zapier integration monitoring](https://docs.zapier.com/integrations/build/test-monitoring)
* [Zapier throttling guidance](https://docs.zapier.com/integrations/build/troubleshoot-throttles)
* [Xquik webhook verification](/webhooks/verification)
* [Xquik error handling](/guides/error-handling)
* [Xquik follower export](/guides/follower-export-crm)

## Next steps

Read [API Reference](/api-reference/overview) for statuses, [Webhooks](/webhooks/overview)
for verification, and [Extraction Workflow](/guides/extraction-workflow) for jobs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.