Skip to main content

n8n ↔ Zendesk Web Widget (Classic) – JWT Integration

Internal Documentation v1.0 – 2025-08-25
Goal
Allow visitors authenticated through your n8n chat front-end to start a Zendesk Web Widget session, while still requiring a human agent to approve any ticket creation.

1. Prerequisites

Item Where to find
Zendesk account with Web Widget (Classic) enabled Admin Center → Channels → Widget
Shared Secret for JWT Admin Center → Channels → Chat → Widget → Authentication
n8n instance reachable from the public internet https://your-n8n.com
Existing n8n workflow that pauses for human review (Human-in-the-Loop)

2. High-Level Flow

  1. Visitor loads your web-chat.
  2. Front-end requests a JWT from n8n.
  3. n8n signs and returns the JWT.
  4. Zendesk Web Widget starts an authenticated chat session.
  5. Human-in-the-Loop still controls ticket creation (Wait node).

3. n8n Endpoints

3.1 JWT Issuer (POST /webhook/zendesk-jwt)

Purpose: Zendesk will call this endpoint to verify the visitor.

Workflow Steps

Node Settings
Webhook Path = /webhook/zendesk-jwt (POST)

Lookup User Any node that confirms the user_token sent by Zendesk is valid (Database, Google Sheets, etc.).
JWT Sign Algorithm = HS256





Respond to Webhook Status = 200

Example cURL

curl -X POST \
  'https://your-n8n.com/webhook/zendesk-jwt?user_token=abc123' \
  -H 'Content-Type: application/json'

Expect:
{"jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}

3.2 Optional Token Generator for Front-End (GET /webhook/chat-token)

If your front-end needs to fetch the token itself (instead of letting Zendesk call the endpoint directly), create a second simple workflow:
Node Settings
Webhook Method = GET
JWT Sign Same payload & secret as above
Respond to Webhook Body = {"jwt":"{{ $('JWT Sign').item.jwt }}"}

4. Zendesk Configuration

Admin Center → Channels → ChatWidgetAuthentication
Field Value
Authentication Method JWT
JWT URL https://your-n8n.com/webhook/zendesk-jwt
JWT Secret Paste the same Shared Secret used in n8n

5. Front-End Snippet

Add this after the Zendesk Web Widget script is loaded:
<script>
  // Replace with your n8n endpoint if you created /webhook/chat-token
  fetch('/api/n8n/get-chat-token', { credentials: 'include' })
    .then(r => r.json())
    .then(({ jwt }) => {
      zE('webWidget', 'chat:setJwtFn', callback => callback(jwt));
    });
</script>
If you let Zendesk call your endpoint directly, omit the fetch and simply use the JWT URL configured above.

6. Human-in-the-Loop Remains Intact

Your existing workflow already has a Wait node that pauses until an agent approves.
Nothing in the JWT flow changes that—ticket creation only proceeds after the webhook resume call.

7. Security Checklist

  • [ ] HTTPS only (n8n & Zendesk endpoints).
  • [ ] Rotate Shared Secret periodically → update both Zendesk and n8n JWT node.
  • [ ] Log every JWT issuance (iat, IP, user_token).
  • [ ] Validate user_token strictly; deny unknown tokens immediately.

8. Troubleshooting Quick-Table

Symptom Likely Cause Fix
Widget shows “Unable to authenticate” Wrong Shared Secret or expired iat Check secret & timestamp
404 from Zendesk Wrong JWT URL Ensure https://your-n8n.com/webhook/zendesk-jwt is publicly reachable
“Invalid JWT format” Payload missing required claims Ensure nameemailjtiiat are present

9. Change Log

Date Author Notes
2025-08-25 DevOps Initial draft based on Zendesk article 4408838925082