Drop Ori into your product in two steps

Copy a snippet, paste it, talk to your customers. The widget bundle does the rest.

Web widget

Drop this script tag into the <body> of any page on your site. Replace YOUR_KEY_HERE with the widget key from your dashboard (Settings → Widget).

<script src="https://cdn.quikori.com/widget.js" data-widget-key="YOUR_KEY_HERE" data-api-base="https://cdn.quikori.com"></script>

The widget self-mounts inside an open Shadow DOM (no CSS conflict with your host page) and auto-detects mobile to render a full-screen takeover under 640px.

API quickstart

Already have a backend? Stream chats directly via the API. Auth with either a widget key (X-Widget-Key header) or a session JWT.

Stream a chat (curl)

curl -X POST https://api.quikori.com/v1/chat/stream \
  -H "Content-Type: application/json" \
  -H "X-Widget-Key: YOUR_KEY_HERE" \
  -d '{"message": "What is your return policy?", "conversation_id": "abc-123"}'

Request body (JWT auth)

{
  "message": "What is your return policy?",
  "conversation_id": "abc-123",
  "session_token": "Bearer ${SESSION_JWT}"
}

Knowledge base sources

Three ingestion paths from the dashboard /dashboard/knowledge-base — Files tab uploads, URL tab single-page crawls, Sitemap tab full-site crawls.

Supported file types

  • PDF — text-layer extracted via PyMuPDF; OCR for scanned pages on Professional Team and above.
  • Plain text (.txt, .md) — UTF-8 only.
  • HTML / web pages — readable-content extraction (boilerplate stripped).
  • Sitemap.xml — crawls every entry; depth + same-origin restricted.

Limits

  • File size: 50MB per file (PDFs > 50MB rejected at presigned-URL issue).
  • Source count: per-tier (see Pricing).
  • Chunk size: 1000 characters with 200-char overlap (RecursiveCharacterTextSplitter).
  • Embedding model: text-embedding-3-small (1536 dimensions).

Ingestion lifecycle

  • Upload → S3 presigned PUT → background ARQ worker picks up → text extract → chunk → embed → write to pgvector.
  • Status surfaced in real time on the source row (uploading → processing → ready / failed).
  • On failure, retry from the row action menu; transient errors auto-retry up to 3×.

WhatsApp Business setup (Meta Cloud API)

End-to-end runbook to wire your WhatsApp Business phone number to Quikori. Estimated time: 30-45 minutes including Meta's app-review flow.

  1. Create a Meta App. Go to developers.facebook.com → My Apps → Create App → Business → Continue. Pick a display name (e.g., “YourCompany Quikori Bot”) and a contact email.
  2. Add the WhatsApp product.In your new app's dashboard, find the “Add Products” panel and click Set up on WhatsApp. This creates a WhatsApp Business Account (WABA) tied to your Meta App and provisions a test phone number you can use immediately for development.
  3. Verify your business + add a real phone number. For production, complete Business Verification in Meta Business Suite (legal entity name, address, tax ID, website, authorized rep). After verification, add your real phone number under WhatsApp → Phone Numbers → Add phone number and complete OTP verification.
  4. Note the IDs Quikori needs. From WhatsApp → API Setup, copy:
    • phone_number_id — your WhatsApp Business phone number ID.
    • whatsapp_business_account_id — your WABA ID.
    • System User access token with whatsapp_business_messaging + whatsapp_business_management scopes. Generate via Meta Business Suite → Business Settings → System Users → Add → assign WABA + generate token. Pick a token without expiration for production (you'll rotate manually).
  5. Configure the webhook in Meta. In your Meta App, go to WhatsApp → Configuration → Webhook → Edit:
    • Callback URL — the value shown in your Quikori dashboard at /dashboard/whatsapp under “Webhook URL”. Format: https://api.your-domain/api/v1/whatsapp/webhook
    • Verify Token— also shown in your Quikori dashboard under “Verify Token”. This is a platform-level value Quikori provides to all tenants. Paste it verbatim.
    • Click Verify and save. Meta makes a GET request to your callback URL with this token; Quikori's backend validates and responds with the challenge.
    • Subscribe to webhook fields: messages (always required), plus message_template_status_update and message_status if you want delivery receipts surfaced in Quikori analytics.
  6. Paste credentials into Quikori. In your Quikori dashboard at /dashboard/whatsapp:
    • Phone Number ID → from step 4
    • Access Token → from step 4 (envelope-encrypted at rest, never displayed back to you)
    • Click Test connection — Quikori sends a Meta API health probe. Green check ✓ means setup is complete.
  7. Send a test message. From your real phone, message your WhatsApp Business number. Within 1-2 seconds Ori replies grounded in your knowledge base. Conversation appears in /dashboard/chat-history tagged with the WhatsApp channel.
Production gotchas:
  • Message templates — Meta requires pre-approved templates for the FIRST outbound message in a session-out window. Quikori auto-handles inbound replies (24-hour customer-service window); for proactive outbound, configure templates in Meta Business Suite first.
  • Rate limits — Tier 1 limit is 1,000 messages / 24h after verification. Higher tiers unlock as your business demonstrates good message-quality scores.
  • Token rotation — System User tokens don't auto-expire but Meta may invalidate on policy violations. Set a calendar reminder to rotate every 6-12 months.
  • Webhook signature — Quikori verifies every incoming webhook with HMAC-SHA256 against your App Secret. Set META_APP_SECRET in your environment so verification works.

Quikori uses the official WhatsApp Business Cloud API hosted by Meta. We don't resell On-Premises BSP licenses; if your business is already on a BSP (Twilio, MessageBird, 360dialog), migrate the number to Cloud API first. Meta's migration guide: developers.facebook.com → migrate-existing-whatsapp-business-account.

Stripe payments setup (live mode)

Quikori's billing surface is Stripe-native. Quikori never sees card numbers — Stripe Checkout handles PCI-compliant collection; Quikori stores only the customer ID + subscription metadata.

  1. Create a Stripe account. Sign up at stripe.com (use your business email). Complete the account-activation checklist (legal entity, bank account, tax info) — Stripe won't allow live-mode charges until done.
  2. Create 3 Products in Stripe Dashboard → Products → Add product:
    • Support Team
    • Professional Team
    • Enterprise Team (custom-priced; create with a default price as a starting point — adjust per-customer in custom contracts)
  3. Add 2 Prices per Product (Monthly + Annual):
    • Recurring → Monthly billing → set the per-month rate
    • Recurring → Yearly billing → set the per-year rate (at the “2 months free” discount baked into the marketing prices on /pricing: monthly_rate × 10 = annual rate)
    Result: 6 Price IDs total (3 tiers × 2 cadences). Format price_1ABcdEFghIJ23kLmNo45PQ.
  4. Configure the webhook endpoint. Stripe Dashboard → Developers → Webhooks → Add endpoint:
    • Endpoint URL: https://api.your-domain/api/v1/stripe/webhook
    • Events to send: checkout.session.completed, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed, invoice.paid.
    • Copy the Signing secret (starts with whsec_...) — Quikori needs this to verify webhook authenticity.
  5. Configure the Customer Portal. Stripe Dashboard → Settings → Billing → Customer Portal:
    • Enable: update payment method, switch plans, cancel, view invoices.
    • Allowed products: the 3 Quikori products from step 2.
    • Cancellation: immediate OR end-of-period (we recommend end-of-period — matches the “keep access until period end” promise in /pricing FAQ).
  6. Drop the IDs into your Quikori environment. Set in your .env (production) or web/.env.local (dev):
    STRIPE_SECRET_KEY=sk_live_...
    STRIPE_WEBHOOK_SECRET=whsec_...
    STRIPE_PRICE_ID_SUPPORT_TEAM_MONTHLY=price_...
    STRIPE_PRICE_ID_SUPPORT_TEAM_ANNUAL=price_...
    STRIPE_PRICE_ID_PROFESSIONAL_TEAM_MONTHLY=price_...
    STRIPE_PRICE_ID_PROFESSIONAL_TEAM_ANNUAL=price_...
    STRIPE_PRICE_ID_ENTERPRISE_TEAM_MONTHLY=price_...
    STRIPE_PRICE_ID_ENTERPRISE_TEAM_ANNUAL=price_...
    Restart the API + web services for env vars to take effect.
  7. Test the flow before going live. Switch to Stripe Test Mode (top-right toggle) and run a Checkout with test card 4242 4242 4242 4242 (any future expiry, any 3-digit CVC, any postal code). Verify:
    • Checkout redirects back to /dashboard/billing?success=1
    • Toast: “Subscription activated. Welcome to Support Team!”
    • PlanCard shows the new tier + period dates
    • Customer Portal opens via the “Manage billing” CTA
  8. Switch to live mode.Flip the Stripe Dashboard toggle from Test → Live. Re-create products + webhook in live mode (Stripe doesn't copy them over). Update your env vars tosk_live_... + the live Price IDs + live webhook secret. Repeat the test flow with a real card.
Production gotchas:
  • Tax — Stripe Tax can auto-calculate VAT/GST/sales tax. Enable in Settings → Tax. Without it, tenants see pre-tax pricing and you may face compliance gaps.
  • Webhook reliability — Stripe retries failed webhooks for up to 3 days. Quikori's webhook handler is idempotent (dedupes via stripe_event_id uniqueness) so retries are safe.
  • Past-due banner — when Stripe's 3-retry payment dunning fails, the subscription transitions to past_due. /dashboard/billing surfaces a banner with a one-click “Update payment” CTA that opens Customer Portal.
  • Refunds — Issue from Stripe Dashboard → Payments → find charge → Refund. Quikori's billing UI shows refunded line items in the invoice list automatically.

Full reference

Browse every endpoint with request/response shapes in the Swagger reference. Endpoint groups: /api/v1/auth/*, /api/v1/sources/*, /api/v1/chat/*, /api/v1/billing/*, /api/v1/whatsapp/*, /api/v1/widget/*.