# ConveRact developer integration guide

https://www.converact.com/developers

Free service integration. No installation charge. No one-time fee.

ConveRact charges no service integration or setup fee. Your selected subscription, usage charges, and third-party provider fees still apply. API and channel availability depend on your plan and provider account.

## 1. Choose your integration path

Use the website widget to add customer-facing AI conversations to your site. Use a workspace API key from your server to read your catalog, quotes, and payments or create a payment link for an existing quote. Use webhooks to receive workspace events. Connect social and voice providers through Channels and Settings.

These are different authentication paths. A widget public key cannot read private workspace data. A workspace API key does not authenticate the tenant administration screens or provide a generic endpoint for running every AI service. Public AI service demo endpoints are demonstrations, not a production replacement for your configured tenant services.

Examples use placeholder keys and IDs. Replace them with values from your own workspace. All API paths below are relative to https://api.converact.com/api/v1. Requests and ordinary responses use JSON; timestamps use ISO 8601. Do not use another tenant’s IDs or the ConveRact website’s public key.

1. Sign in as Tenant Admin. Complete onboarding, activate the appropriate subscription, and confirm the workspace is active.
2. Add your Business profile, approved Knowledge, and Products or services. Publish only accurate, active offerings.
3. Configure your AI provider and the required channels in the workspace. Test your service in training mode before enabling customer traffic.
4. Choose Widget, API keys, or Webhooks in the workspace and follow the matching section below. API key creation requires a plan with API access.

## 2. Install the website widget

Open Widget, add each exact website origin, and copy your tenant public key. An origin is a scheme and hostname, optionally with a port: https://shop.example.com. Add staging and localhost origins separately; do not enter a path or assume a wildcard is supported.

Place the following before the closing body tag. Load the script before calling init; do not add async to this example. The public key may appear in website code. Never place a workspace API key, model key, or provider secret in this snippet.

AISalesWidget is the supported JavaScript global. Its existing name is retained for compatibility. init accepts tenantKey and an optional apiUrl, and returns an instance with destroy(). In a single-page app, initialize once and call destroy() when removing the widget. The widget handles customer identity prompts, conversation state, and supported commerce actions.

```html
<script src="https://www.converact.com/widget.js"></script>
<script>
  const widget = AISalesWidget.init({
    tenantKey: "YOUR_TENANT_PUBLIC_KEY",
    apiUrl: "https://api.converact.com/api/v1"
  });
  // When removing the widget: widget.destroy();
</script>
```

1. Open your allowed website in a private browser window and confirm your own company name and branding appear.
2. Ask about an active offering and verify the reply uses your approved business information.
3. Confirm the conversation appears in your workspace and test human takeover.
4. If your site uses Content Security Policy, allow the widget script origin and API connection origin. The widget injects styles; ensure your style policy permits them. Add provider-specific media or connection origins only for features you enable.

## 3. Create a server API key and make your first request

Open API keys → Generate key as Tenant Admin. Give the key a descriptive integration name and select only the permissions you need. Copy the secret immediately: it is displayed once. Store it in your server’s secret manager or environment as CONVERACT_API_KEY.

Send Authorization: Bearer <secret>. Keys currently begin with atl_; keep the issued key unchanged. The server resolves your tenant from the key, so do not send a tenantId to select a workspace. Workspace cookies and CSRF tokens are not required for these /external routes.

Run the following in a POSIX shell on your server after setting CONVERACT_API_KEY securely. A successful request returns success: true and data.items with data.pagination. An empty items array is valid. Never call private workspace APIs directly from public browser code.

```sh
curl --fail-with-body --get \
  'https://api.converact.com/api/v1/external/products' \
  --header "Authorization: Bearer $CONVERACT_API_KEY" \
  --data-urlencode 'page=1' \
  --data-urlencode 'limit=25'
```

1. Start with products:read and verify you receive only your own tenant’s catalog.
2. Store the key only on the server; redact Authorization headers from logs and error reports.
3. To rotate, generate a replacement, update your server, test a request, and revoke the old key. Revocation takes effect for subsequent requests.

## 4. Read catalog, quote, and payment data

GET /external/products supports search (maximum 120 characters), category (exact match, maximum 80 characters), page (integer, minimum 1, default 1), and limit (integer, 1–100, default 25). Pagination is returned in data.pagination as page, limit, total, and pages. Products include non-archived entries, which can include drafts; filter status === ACTIVE before showing an external catalog to customers.

GET /external/products/{id} returns a product in data. GET /external/quotes and GET /external/payments return arrays directly in data, without the product pagination wrapper. GET /external/payments/{id} returns one payment. Treat record IDs as opaque strings and URL-encode them in paths.

The Node.js example uses built-in fetch and reads every catalog page. It handles non-JSON errors, applies a timeout, and does not automatically repeat a write request. Product field availability depends on the offering type; use the returned currency, pricingMode, and status rather than assuming every offering is a stocked product with a fixed price.

```javascript
const base = "https://api.converact.com/api/v1";
const key = process.env.CONVERACT_API_KEY;
if (!key) throw new Error("Set CONVERACT_API_KEY on the server");

async function get(path) {
  const response = await fetch(base + path, {
    headers: { Authorization: "Bearer " + key },
    signal: AbortSignal.timeout(30000)
  });
  const body = await response.json().catch(() => null);
  if (!response.ok || !body?.success) {
    throw new Error(body?.error?.code || "HTTP_" + response.status);
  }
  return body.data;
}

const activeProducts = [];
for (let page = 1; ; page += 1) {
  const data = await get("/external/products?page=" + page + "&limit=100");
  activeProducts.push(...data.items.filter(item => item.status === "ACTIVE"));
  if (page >= data.pagination.pages) break;
}
console.log("Active offerings:", activeProducts.length);
```

## 5. Create a payment link for an existing quote

Configure your customer payment provider in Settings → Customer payments first, and use an existing quote from your own workspace. POST /external/quotes/{id}/payment-link requires payment-links:write and returns HTTP 201 with the created payment in data. There is no external quote-creation endpoint in the documented API.

The body accepts only conversationId (4–160 characters), customerName (2–100), customerEmail (email address), customerPhone (8–24), billingFrequency (ONE_TIME or MONTHLY; default ONE_TIME), and billingCycles (integer 1–1200). All fields are optional; an empty object uses the available quote context. Do not send a price, tenantId, or unsupported property.

In production, missing credentials return a setup error and TEST mode returns LIVE_PAYMENT_PROVIDER_REQUIRED. Creating a link leaves it OPEN. Only server-side provider verification or a validated live webhook can confirm payment; never show success just because a link was created or the browser returned status=success.

ONE_TIME describes the customer payment frequency, not an integration fee. Subscription-style customer payments depend on the configured payment provider. Use the returned payment details and workspace payment status to confirm the result; do not treat a browser redirect as proof of payment.

No generic Idempotency-Key contract is documented for this endpoint. If a write times out, reconcile through GET /external/payments or the workspace before trying again. Repeating the request can create another payment link.

```sh
curl --fail-with-body \
  'https://api.converact.com/api/v1/external/quotes/YOUR_QUOTE_ID/payment-link' \
  --header "Authorization: Bearer $CONVERACT_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{"billingFrequency":"ONE_TIME"}'
```

## 6. Build a custom text-chat interface

For a custom browser UI, POST /public/widget/{publicKey}/chat accepts message (required, trimmed, 1–2000 characters), conversationId (optional), customerId (optional, maximum 128 characters), and clientMessageId (optional, 8–160 characters). Browser requests must originate from an allowed website. Use a random per-visitor customer ID, not an email address or sequential customer number.

The response has data.conversation.id, data.conversation.customerId, data.message.text, and potentially data.products and data.message.action. Save the returned conversation and customer IDs for later turns. Never share a visitor’s stored conversation with another visitor. Render reply text as text, not unsanitized HTML.

Create a new clientMessageId for each new message and reuse it only when retrying that same message and conversation. A conflicting in-flight request can return CHAT_REQUEST_CONFLICT. Disable duplicate sends while awaiting a response.

This example covers text chat only. If data.message.action requests contact verification or another action your UI does not implement, show a clear next step and use the supported widget flow; do not bypass verification. The shipped widget is the complete supported client for identity and commerce interactions.

```javascript
const publicKey = "YOUR_TENANT_PUBLIC_KEY";
const storageKey = "converact-chat:" + publicKey;
let state = JSON.parse(sessionStorage.getItem(storageKey) || "null") || {
  customerId: crypto.randomUUID()
};

async function sendText(message, clientMessageId = crypto.randomUUID()) {
  const response = await fetch(
    "https://api.converact.com/api/v1/public/widget/" +
      encodeURIComponent(publicKey) + "/chat",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ ...state, message, clientMessageId })
    }
  );
  const body = await response.json().catch(() => null);
  if (!response.ok || !body?.success) {
    throw new Error(body?.error?.code || "HTTP_" + response.status);
  }
  state = {
    customerId: body.data.conversation.customerId,
    conversationId: body.data.conversation.id
  };
  sessionStorage.setItem(storageKey, JSON.stringify(state));
  return body.data; // Display message.text and handle message.action.
}
```

## 7. Receive and verify workspace webhooks

Open Webhooks in your tenant workspace. Add a publicly reachable HTTPS receiver, select events, and securely store the signing secret displayed on creation. Enable the endpoint. Available events are conversation.created, conversation.resolved, lead.created, quote.created, handover.requested, and customer.created.

The JSON envelope contains id, type, tenantId, createdAt, and data. Event data is a change notification, not a promise of a full record. Verify the signature against the exact raw request bytes before parsing JSON. The headers are X-Atlas-Signature, X-Atlas-Timestamp, and X-Atlas-Event-Id; those compatibility names must not be renamed.

The signature is sha256= followed by the hexadecimal HMAC-SHA256 of timestamp + "." + raw body, using your signing secret. Reject malformed or stale timestamps; the example allows five minutes. Compare signatures using a constant-time function, and check that the header event ID matches the JSON id.

After verification, durably enqueue the event and return a 2xx response promptly. Deduplicate event IDs with a persistent unique constraint before applying business effects. Do not rely on process memory. Keep event processing safe for retries and out-of-order deliveries.

Failed deliveries retry up to eight attempts with exponential backoff capped at one hour. In Webhooks, inspect delivery history, correct the receiver or secret, and retry a failed delivery. Rotating the signing secret requires updating your receiver. Test with a real tenant event; saving an endpoint alone does not prove delivery.

```javascript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyWebhook(rawBody, headers, secret) {
  if (!Buffer.isBuffer(rawBody) || !secret) return false;
  const timestamp = headers["x-atlas-timestamp"];
  const signature = headers["x-atlas-signature"];
  if (typeof timestamp !== "string" || !/^\d+$/.test(timestamp)) return false;
  if (typeof signature !== "string" || !/^sha256=[a-f0-9]{64}$/.test(signature)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = createHmac("sha256", secret)
    .update(timestamp + ".")
    .update(rawBody)
    .digest();
  return timingSafeEqual(Buffer.from(signature.slice(7), "hex"), expected);
}
// Run on the raw body before any JSON middleware changes it.
// Then parse JSON, match its id to x-atlas-event-id, durably enqueue,
// deduplicate, and acknowledge. This helper alone is not a receiver.
```

## 8. Troubleshoot requests without a support ticket

Ordinary success responses use { "success": true, "data": ... }. Application errors use { "success": false, "error": { "code": "...", "message": "..." } }; validation errors can also include error.details. Proxies and rate limiters may return plain text, so check HTTP status and handle non-JSON bodies.

400 VALIDATION_ERROR: compare body fields, types, lengths, and pagination with this guide. 401 API_KEY_REQUIRED, API_KEY_INVALID, or API_KEY_EXPIRED: verify the Bearer header and replace invalid, revoked, or expired credentials.

403 API_PERMISSION_REQUIRED: generate a key with the required permission. 403 TENANT_UNAVAILABLE: check workspace status. 403 ORIGIN_NOT_ALLOWED: add the exact browser origin in Widget. A browser CORS failure can occur before an application JSON error; inspect the network response and use the approved origin.

402 FEATURE_NOT_INCLUDED during API key creation: your plan must include API access. Free integration does not override plan entitlements. 404 PRODUCT_NOT_FOUND, PAYMENT_NOT_FOUND, WIDGET_NOT_FOUND, or CONVERSATION_NOT_FOUND: check that the record, tenant key, and visitor state belong to the same active workspace.

409 CHAT_REQUEST_CONFLICT: use a fresh clientMessageId for a different message. For other 409 responses, read the error code and reconcile the resource state. 429: honor Retry-After when supplied, reduce concurrency, and retry reads with bounded exponential backoff and jitter. No universal per-tenant rate limit is promised.

For 5xx or network failures, show a recoverable error, check https://api.converact.com/health, and retry safe reads with a bounded delay. Reconcile payment writes before retrying. Record timestamp, path, HTTP status, and error code for debugging; exclude keys, tokens, and customer message content from logs.

## 9. Connect channels and launch

Open AI Services in your workspace and review the launch checklist. It refreshes saved configuration every 30 seconds and links to each setup screen. Configured means the settings are present; it does not prove a provider has completed a live action. Use the channel guides below for WhatsApp, Instagram, Facebook Messenger, Telegram, website chat, email, SMS, and voice. Each guide lists the tenant workspace steps and provider requirements. You can follow these steps directly; you do not need to book a ConveRact installation call.

You still need ownership or administrator access to the external provider accounts, the applicable provider approvals, and any required credentials. SMS may need a supported transport adapter; voice needs configured voice and telephony infrastructure. Do not mark a provider live before its connection test succeeds.

Integration and installation are free of ConveRact setup fees. Your subscription, usage, and external provider charges still apply. Before launch, review your plan’s API and channel entitlements and the provider’s own billing settings.

1. Confirm tenant branding, allowed origins, approved knowledge, and active offerings.
2. Verify a scoped server read and a denied request with a missing permission.
3. Test a complete conversation, human handover, and any enabled verification or payment flow using your provider’s test mode first.
4. Verify a signed webhook, reject a modified body, and deduplicate a repeated event.
5. Test mobile display, credential rotation, provider failure, and timeouts. Enable customer traffic only after these checks pass.

## Server API reference

GET /external/products — products:read. Paginated catalog in data.items and data.pagination.

GET /external/products/{id} — products:read. One tenant product in data.

GET /external/quotes — quotes:read. Tenant quotes as an array in data.

GET /external/payments — payments:read. Tenant payments as an array in data.

GET /external/payments/{id} — payments:read. One tenant payment in data.

POST /external/quotes/{id}/payment-link — payment-links:write. Create a payment for an existing quote; HTTP 201.

OpenAPI: https://www.converact.com/developer-api.openapi.json

## Channel setup guides

[WhatsApp Business](https://www.converact.com/blog/whatsapp-ai-sales-launch-guide)

[Instagram Direct](https://www.converact.com/blog/instagram-direct-ai-connection-guide)

[Facebook Messenger](https://www.converact.com/blog/facebook-messenger-ai-connection-guide)

[Telegram bot](https://www.converact.com/blog/telegram-ai-bot-connection-guide)

[Website, email, SMS, voice, and custom apps](https://www.converact.com/blog/converact-complete-channel-connection-handbook)

[Voice agents](https://www.converact.com/blog/voice-agents-elevenlabs-production-checklist)
