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.
- 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.
- 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.
- 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.
- 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_managementscopes. 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).
- 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/whatsappunder “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.
- Callback URL — the value shown in your Quikori dashboard at
- 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.
- 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-historytagged with the WhatsApp channel.
- 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_SECRETin 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.
- 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.
- 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)
- 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)
price_1ABcdEFghIJ23kLmNo45PQ. - 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.
- Endpoint URL:
- 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).
- Drop the IDs into your Quikori environment. Set in your
.env(production) orweb/.env.local(dev):
Restart the API + web services for env vars to take effect.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_... - 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
- Checkout redirects back to
- 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 to
sk_live_...+ the live Price IDs + live webhook secret. Repeat the test flow with a real card.
- 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_iduniqueness) 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/*.