The swap, in three steps
- Sign up, add your website in Pidge and list its domains. The website ID is on the Websites page in the inbox.
- In your page, keep
window.$crisp = []andCRISP_WEBSITE_ID. Put your Pidge website ID inCRISP_WEBSITE_ID. - Change the script's address from Crisp's to Pidge's.
Here is Crisp's usual snippet, before and after:
<!-- before --> <script> window.$crisp = []; window.CRISP_WEBSITE_ID = "your-crisp-website-id"; (function () { var d = document, s = d.createElement("script"); s.src = "https://client.crisp.chat/l.js"; s.async = 1; d.getElementsByTagName("head")[0].appendChild(s); })(); </script> <!-- after --> <script> window.$crisp = []; window.CRISP_WEBSITE_ID = "your-pidge-website-id"; (function () { var d = document, s = d.createElement("script"); s.src = "https://app.pidge.chat/client/l.js"; s.async = 1; d.getElementsByTagName("head")[0].appendChild(s); })(); </script>
Replace Crisp's script rather than adding Pidge next to it. Both read
window.$crisp, so running the two together gives you two bubbles fighting over
the same calls.
If you set up a chat domain like chat.example.com,
the script address is https://chat.example.com/client/l.js instead. The code
on the Websites page always shows the right one.
What keeps working on the page
Pidge installs one object as both window.$pidge and window.$crisp.
Calls pushed before the script loads wait in the array and run once it has, as with Crisp.
| Crisp call | In Pidge |
|---|---|
["set", "user:email", [email, signature]] | Works. The signature is optional and checked the same way. See signed emails. |
["set", "user:nickname", [name]] | Works. |
["set", "session:data", [[[key, value], …]]] | Works. One pair or a list of pairs. Values are stored as text. |
["set", "session:segments", [[…], overwrite]] | Works, including overwrite. |
["do", "chat:open"], chat:close, chat:toggle | Works. |
["do", "chat:show"], chat:hide | Works. |
["do", "message:send", ["text", "…"]] | Works for text. Other message types are ignored. |
["do", "message:show", ["text", "…"]] | Works for text. Other message types are ignored. |
["do", "session:reset"] | Works. Forgets the visitor and starts an empty chat. |
$crisp.is(…) | Works for chat:opened, chat:closed, chat:visible, chat:hidden and session:ongoing. Anything else returns false. |
What the widget ignores
Pidge skips commands it doesn't know, without an error, as Crisp does. That includes:
- Event callbacks:
["on", …]and["off", …]. Your handler never runs. user:phone,user:avatar,user:companyandsession:event.["config", …]options. Colours, position and texts are set per website in the inbox instead.- Other Crisp globals, like
CRISP_TOKEN_IDandCRISP_RUNTIME_CONFIG. To keep a logged-in customer in one chat across devices, use signed emails instead of a token.
One thing does break: Pidge has no $crisp.get(…). Calling it throws a
TypeError, so search your code for $crisp.get and remove or
guard those calls before you switch.
Signed emails
Crisp calls this identity verification. Pidge uses the same scheme: HMAC-SHA256 of the email, keyed with a secret, as lowercase hex. Your backend code stays as it is. Only the secret changes.
- In the Pidge inbox, open Developers and create a secret under Signed visitor emails for the website. It's shown once.
- Put it where your backend keeps the Crisp secret today.
- Keep pushing the email and signature as before:
$crisp.push(["set", "user:email", ["ada@example.com", "<signature from your server>"]]);
A signed email shows as verified in the inbox, and a customer who switches from phone to laptop stays in the same chat.
Your backend: the REST API
The paths, Basic auth and { error, reason, data } responses follow Crisp's
REST API v1. Change the base URL and the key:
# before CRISP_API=https://api.crisp.chat/v1 # after CRISP_API=https://app.pidge.chat/v1
Keys are made on the Developers page, which only owners can open. Send the
identifier as the user name and the key as the password. If your client sends
X-Crisp-Tier: plugin, Pidge accepts it and ignores it. These paths exist,
all under /v1/website/{website_id}:
| Path | What it does |
|---|---|
GET /conversations/{page} | Lists conversations, 20 a page. |
GET /conversation/{session_id} | One conversation. |
GET /conversation/{session_id}/messages | Messages, with timestamp_before to page back. |
POST /conversation/{session_id}/message | Sends a message or a note. |
DELETE /conversation/{session_id}/message/{fingerprint} | Deletes a message. |
PATCH /conversation/{session_id}/compose | Starts or stops the typing indicator. |
GET and PATCH /conversation/{session_id}/meta | Reads or changes name, email, data and segments. |
GET /conversation/{session_id}/pages/{page} | The pages the visitor opened, newest first. |
GET and PATCH /conversation/{session_id}/state | Reads or sets pending, unresolved or resolved. |
PATCH /conversation/{session_id}/block | Blocks or unblocks the visitor. |
GET /people/profiles/{page} | Finds visitors by name or email. |
That's the whole list. Crisp has many more endpoints, so if your integration calls one that isn't here, it gets an error. Tell us which one in the chat on this page. The API docs have the fields, limits and what each plan includes.
Webhooks
Add a webhook on the Developers page. The body is Crisp's shape,
{ website_id, event, data, timestamp }, and the
X-Crisp-Signature header is hex HMAC-SHA256 of
[timestamp;body]. verifyHook from Crisp's Node library accepts it.
Your receiver needs the new signing secret and nothing else.
Pidge sends message:send, message:received,
message:removed, session:set_email,
session:set_nickname, session:set_data,
session:set_segments, session:set_state,
session:sync:pages and session:removed. You can also pick
session:set_block, but it isn't sent yet. Failed deliveries are retried for
about 23 hours.
What doesn't carry over
- Past conversations. Pidge doesn't import your Crisp history. Visitors start with an empty chat, and old chats stay in Crisp. Keep the Crisp account, or save what you need from it, before you cancel.
- Widget settings. The colour, support hours and other settings are made again in Pidge, on the Websites page.
- Other channels. Crisp also handles email, WhatsApp, Instagram, Messenger and more in one inbox. Pidge is website chat, plus email replies to visitors who have left.
- Crisp's extras. The knowledge base, status page, campaigns, chatbot builder and Hugo, its AI agent. Video and audio calls, and co-browsing.
- Apps and plugins. Crisp has native apps for iPhone, Android, Windows and Mac, and plugins for shops and CRMs. Pidge's inbox runs in the browser and installs to your home screen.
If you use any of these every day, Crisp is probably still the better fit. Pidge vs Crisp sets the two side by side.
What it costs
Crisp charges per workspace with a set number of seats: Free, then Mini at $45, Essentials at $95 or Plus at $295 a month, VAT not included. Pidge is free for one website with two people replying. After that it's $10 per website a month, or $100 a year, however many people answer.
- One website, three or four people: Crisp Mini, $45. Pidge, $10.
- One website, five to ten people: Crisp Essentials, $95. Pidge, $10.
- One or two people: both have a free plan, so pick on features.
Crisp's prices are from crisp.chat/en/pricing, checked 16 October 2026.
Next
- API docs: every endpoint, webhook and widget command.
- Install guides: where the snippet goes on WordPress, Shopify, Next.js and others.
- Ad blockers: why
client.crisp.chatis on two blocking lists, and how a chat domain helps.
Moving a site and something doesn't behave like Crisp did? Tell us in the chat on this page.