Guide

Connect with Warid

Let your users connect their Warid to your app with one button.

Warid puts a business's WhatsApp, Instagram and Messenger conversations in one inbox, with an AI agent that answers customers. "Connect with Warid" works like "Sign in with Google": your user clicks a button, approves, and your app gets a token for their workspace. With it you can read and answer their conversations, and open their Warid dashboard without asking them to log in again.

Building with an AI assistant?
Paste this link into ChatGPT, Claude or Cursor and ask it to add Connect with Warid to your app: https://warid.chat/llms.txt
Your app
Business owner
Warid
/oauth/authorize
Step 1 of 6

Send them to the consent screen

Your app opens Warid's authorize URL in a popup or a redirect, with a PKCE challenge and a random state.

Request
GET https://warid.chat/oauth/authorize  ?response_type=code&client_id=mci_...  &redirect_uri=https://yourapp.com/callback  &scope=conversations:write agent:write  &state=RANDOM&code_challenge=...&code_challenge_method=S256

1. Create your app

In Warid, open Developers and click Create app. Pick a logo (or upload yours), then enter a name and your callback URL, the page people return to after they approve. You get a Client ID (mci_…) and a Client secret (mcs_…). The secret is shown once; keep it on your server.

Create your app in Developers
Create your app in Developers
Your client ID and secret, ready to copy
Your client ID and secret, ready to copy

2. Add the button

When the user clicks your "Connect with Warid" button, make a random state and a PKCE code_verifier, save both in their session, and send them to:

HTTP
https://warid.chat/oauth/authorize  ?response_type=code  &client_id=YOUR_CLIENT_ID  &redirect_uri=https://yourapp.com/warid/callback  &state=RANDOM_STATE  &code_challenge=BASE64URL(SHA256(code_verifier))  &code_challenge_method=S256

They sign in to Warid (or sign up), pick their workspace and approve. Leave out scope to ask for everything your app is enabled for, or pass a space-separated list from the table below.

The consent screen your users see
The consent screen your users see

3. Handle the callback

We send the browser back to your redirect_uri with ?code=…&state=… (or ?error=access_denied). Check that state matches, then swap the code for tokens from your server within 60 seconds:

Terminal
curl -X POST https://api.warid.chat/oauth/token \  -d grant_type=authorization_code \  -d code=THE_CODE \  -d redirect_uri=https://yourapp.com/warid/callback \  -d code_verifier=THE_VERIFIER \  -d client_id=YOUR_CLIENT_ID \  -d client_secret=YOUR_CLIENT_SECRET
Response
{  "access_token": "mat_...",  "token_type": "Bearer",  "expires_in": 3600,  "refresh_token": "mrt_...",  "scope": "conversations:read conversations:write ...",  "workspace_id": "b5d3d605-c177-418a-8e46-acd8c6e9c6f8"}

Save both tokens with your user. The access token lasts an hour. Get a new pair with grant_type=refresh_token&refresh_token=mrt_… (plus your client ID and secret); each refresh token works once.

4. Call the API

Send the access token as a Bearer token. Everything is under https://api.warid.chat/api/v1.

Terminal
# Their conversations, newest firstcurl https://api.warid.chat/api/v1/conversations -H "Authorization: Bearer mat_..." # Reply in onecurl -X POST https://api.warid.chat/api/v1/conversations/CONVERSATION_ID/messages \  -H "Authorization: Bearer mat_..." -H "Content-Type: application/json" \  -d '{"text": "Your order has shipped!"}'

Send messages covers the 24-hour rule, images, WhatsApp templates and verification codes. The API reference lists every endpoint. The OpenAPI document is at https://api.warid.chat/docs/json.

5. Open their dashboard

To send your user into Warid already signed in, ask for a sign-in link when they click, then redirect them to it. A link works once and expires after two minutes, so make a new one on every click.

Terminal
curl -X POST https://api.warid.chat/api/v1/sign-in-links \  -H "Authorization: Bearer mat_..." -H "Content-Type: application/json" \  -d '{"path": "/inbox"}'
Response
{ "url": "https://warid.chat/auth/link#token=msl_...", "expiresAt": "2026-10-15T12:02:00.000Z" }

path can be /inbox (the default), /inbox/CONVERSATION_ID, /agent, /channels, /team or /developers. The link signs in the person who connected your app.

Full example

A complete server in one file: the button, the callback, an API call and the dashboard link. Set CLIENT_ID, CLIENT_SECRET and REDIRECT_URI in the environment.

import express from "express";import session from "express-session";import { createHash, randomBytes } from "node:crypto"; const APP = "https://warid.chat";const API = "https://api.warid.chat";const { CLIENT_ID, CLIENT_SECRET, REDIRECT_URI } = process.env; const app = express();app.use(session({ secret: "change-me", resave: false, saveUninitialized: true })); // 1. The "Connect with Warid" button links here.app.get("/warid/connect", (req, res) => {  req.session.state = randomBytes(16).toString("hex");  req.session.verifier = randomBytes(32).toString("base64url");  const challenge = createHash("sha256").update(req.session.verifier).digest("base64url");  const url = new URL(`${APP}/oauth/authorize`);  url.search = new URLSearchParams({    response_type: "code",    client_id: CLIENT_ID,    redirect_uri: REDIRECT_URI,    state: req.session.state,    code_challenge: challenge,    code_challenge_method: "S256",  });  res.redirect(url.toString());}); // 2. Warid sends the user back here.app.get("/warid/callback", async (req, res) => {  if (req.query.error || req.query.state !== req.session.state) return res.status(400).send("Not connected");  const tokens = await fetch(`${API}/oauth/token`, {    method: "POST",    body: new URLSearchParams({      grant_type: "authorization_code",      code: req.query.code,      redirect_uri: REDIRECT_URI,      code_verifier: req.session.verifier,      client_id: CLIENT_ID,      client_secret: CLIENT_SECRET,    }),  }).then((r) => r.json());  req.session.warid = tokens; // Save with your user in your database.  res.redirect("/");}); // 3. Use the API.app.get("/warid/conversations", async (req, res) => {  const data = await fetch(`${API}/api/v1/conversations`, {    headers: { Authorization: `Bearer ${req.session.warid.access_token}` },  }).then((r) => r.json());  res.json(data.items);}); // 4. "Open Warid": straight into their dashboard.app.get("/warid/open", async (req, res) => {  const link = await fetch(`${API}/api/v1/sign-in-links`, {    method: "POST",    headers: { Authorization: `Bearer ${req.session.warid.access_token}`, "Content-Type": "application/json" },    body: JSON.stringify({ path: "/inbox" }),  }).then((r) => r.json());  res.redirect(link.url);}); app.listen(3000);

Permissions

ScopeLets your app
conversations:readList conversations and read messages
conversations:writeSend messages and notes; change status, assignee and tags
contacts:readRead contacts
contacts:writeRename contacts
channels:readSee the connected WhatsApp, Instagram and Messenger accounts
agent:readRead the AI agent's settings and knowledge
agent:writeChange the AI agent's instructions and knowledge
webhooks:readList webhook endpoints
webhooks:writeAdd and remove webhook endpoints
templates:readList WhatsApp message templates and their review status
templates:writeCreate and delete WhatsApp message templates
otp:sendSend verification codes on WhatsApp and check them
dashboard:openOpen the dashboard for the person who connected the app

Good to know

  • Testing. A new app connects only to your own workspaces. When it's ready for everyone, open it in Developers and click Submit for review.
  • One app per workspace. A workspace connects one app at a time. The business sees your app's activity in Developers and can disconnect it there; your tokens then return 401.
  • Training the AI agent happens in the dashboard: businesses upload documents and write instructions in AI agent. Your app can link straight there with a sign-in link to /agent.
  • Errors are JSON like { "error": "insufficient_scope", "scope": "agent:write" }. A 401 means the token expired (refresh it) or was revoked. Each connection gets 120 requests a minute.
  • Events. To hear about new messages instead of polling, add webhooks.