Guide to Gorgias Webhooks: Features and Best Practices
Gorgias is a customer support helpdesk built for ecommerce. Its webhooks are HTTP integrations: when a ticket is created, updated or gets a new message, Gorgias calls a URL you configure with a request you template. The same feature can fetch data into the ticket sidebar or push ticket events to your own systems, and it's the second use this guide covers.
This guide covers how Gorgias HTTP integrations are configured, how to template and authenticate the request, how Gorgias's narrow retry rules behave, and the best practices for running a Gorgias receiver in production.
What are Gorgias webhooks?
A Gorgias HTTP integration is a URL, a method, an optional body and a set of ticket triggers. When a trigger fires, Gorgias sends the request, filling in template variables such as {{ticket.id}} and {{ticket.customer.email}} in the URL and body.
There's no fixed payload and no signature. You choose what's in the body, and you authenticate the request either with headers you add, such as Authorization: Bearer <key>, or with OAuth2, where Gorgias fetches a token from your token URL first. The account owner and admins can manage HTTP integrations, on every Helpdesk plan.
Gorgias webhook features
| Feature | Details |
|---|---|
| Configuration | Settings > Account > HTTP integration > Manage > Add HTTP integration |
| Methods | GET, POST, PUT, DELETE |
| Content types | application/json or application/x-www-form-urlencoded (non-GET methods) |
| Body and URL | Templated with Gorgias variables, such as {{ticket.id}} |
| Triggers | Ticket created, updated, self unsnoozed, message created, message failed; or assignment updated; or status updated |
| Signature | None |
| Authentication | Header-based, or OAuth2 |
| Timeout | 5 seconds |
| Retries | Up to 3 (10, 20, 40 seconds), only for unreachable endpoints, and for timeouts with OAuth2 |
| Error responses | Never retried |
| Auto-disable | After 500 consecutive failures |
| Delivery log | Events tab on each integration |
Common events
You choose triggers per integration. The UI names and the keys in the API's integration object are:
| Trigger | API key |
|---|---|
| Ticket created | ticket-created |
| Ticket updated | ticket-updated |
| Ticket message created | ticket-message-created |
| Ticket self unsnoozed | ticket-self-unsnoozed |
| Ticket message failed | ticket-message-failed |
| Ticket assignment updated | ticket-assignment-updated |
| Ticket status updated | ticket-status-updated |
The API object also lists ticket-handed-over, which the help centre doesn't describe. Ticket assignment updated and Ticket status updated are exclusive: selecting either one disables the other triggers on that integration.
Gorgias doesn't add the trigger name to the request, so an integration with several triggers sends the same templated body for all of them. Put the event name in the body yourself and use one integration per trigger:
{
"event": "ticket-created",
"ticket_id": "{{ticket.id}}",
"customer_email": "{{ticket.customer.email}}"
}
event is your own field here. Gorgias's developer documentation lists the full set of template variables you can add.
Setting up Gorgias webhooks
- In your helpdesk, click the Settings icon in the bottom-left corner.
- Under Account, select HTTP integration, open the Manage tab and click Add HTTP integration.
- Enter an Integration name and the URL.
- Set the HTTP Method to
POSTand the request content type toapplication/json, then write the body. - Under Header, add your authentication header (see below).
- Under Triggers, select the ticket events for this integration.
- Click Add integration.
The Events tab on each integration lists the latest requests it sent. Click one to see its details.
Securing Gorgias webhooks
Generate a long random key and add it as a header on the integration, for example:
Authorization: Bearer 3f6b0c2e9a7d4c1b8e5f2a6d9c0b3e7f
On your endpoint, compare the whole header value in constant time:
const crypto = require('crypto');
const express = require('express');
function verifyHeader(value, expected) {
if (typeof value !== 'string' || !expected) return false;
const received = Buffer.from(value, 'utf8');
const wanted = Buffer.from(expected, 'utf8');
return received.length === wanted.length && crypto.timingSafeEqual(received, wanted);
}
const app = express();
app.post('/webhooks/gorgias', express.raw({ type: '*/*' }), (req, res) => {
const key = process.env.GORGIAS_WEBHOOK_KEY;
if (!key) return res.sendStatus(500); // fail closed
if (!verifyHeader(req.headers['authorization'], `Bearer ${key}`)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString('utf8'));
// Route on event.event, then process asynchronously. Gorgias waits 5 seconds at most.
res.sendStatus(200);
});
The same check in Python:
import hmac
import os
def verify_gorgias_header(value) -> bool:
key = os.environ.get("GORGIAS_WEBHOOK_KEY")
if not key or not value:
return False
return hmac.compare_digest(value.encode(), f"Bearer {key}".encode())
The OAuth2 option suits endpoints that already sit behind an OAuth2 token service. Gorgias asks for a Token URL, Client ID, Client Secret, Token Location (header or query string), Token Key and optional Scopes, then sends the token it obtains. Your endpoint would then validate that token rather than a static key. OAuth2 also changes retry behaviour, as described below.
Make Gorgias webhooks production-ready. Hookdeck Event Gateway answers every Gorgias request well inside the 5-second timeout, checks your header, and queues each ticket event, so a slow or failing handler never costs you an event.
Gorgias webhook limitations and pain points
Most failures are never retried
The Problem: Your endpoint returns a 500 or responds slowly, and the ticket event is gone.
Why It Happens: Gorgias retries only in narrow cases. If the endpoint can't be reached at all, it retries up to 3 times, after 10, 20 and 40 seconds. If the endpoint accepts the connection but doesn't answer within 5 seconds, it retries only for OAuth2 integrations; with header-based authentication, it doesn't. Error status codes such as 429 or 500 are never retried, whichever authentication you use.
Workarounds:
- Return a
2xxas soon as the request is stored, well within 5 seconds. - Never return an error for a request you've accepted. Queue it and process it later.
- Check the integration's Events tab after an incident and reconcile tickets with the Gorgias API.
How Hookdeck Can Help: Gorgias's own documentation suggests Hookdeck for this: it "provides queuing, failure recovery, scaling, and monitoring for incoming webhooks". Hookdeck accepts each request immediately, stores it, and retries delivery to your service on your schedule, so a 500 from your code no longer drops the event.
The request doesn't say which trigger fired
The Problem: An integration with several triggers sends you a request, and you can't tell a new ticket from an update.
Why It Happens: The body and URL are your template, and Gorgias doesn't add a trigger name, delivery ID or timestamp.
Workarounds:
- Create one integration per trigger, with the event name in the body or as a query parameter such as
?event=ticket-created. - Include
{{ticket.id}}so you can fetch the current ticket from the API.
How Hookdeck Can Help: Give each integration its own Hookdeck source, or route on your event field with filters, so each trigger reaches the right handler.
Sustained failures disable the integration
The Problem: Ticket events stop arriving and nothing in your logs explains why.
Why It Happens: Gorgias disables an HTTP integration after 500 consecutive failed requests. Any success resets the count, so only a sustained run of failures, such as a long outage, timeouts or a wrong URL, causes it.
Workarounds:
- Watch for Gorgias's notification that an integration was disabled.
- Fix the cause first, then re-enable the integration in Gorgias.
- Keep the endpoint fast enough that timeouts don't count against you.
How Hookdeck Can Help: Hookdeck accepts requests even while your service is down, so Gorgias sees successes and the failure count stays at zero. Your events wait in Hookdeck until your service recovers.
Best practices
Compare the whole header in constant time
Check the full header value, scheme included, with crypto.timingSafeEqual (after a length check) or hmac.compare_digest. Fail closed when the key isn't configured.
Respond within 5 seconds
Store the request, return 2xx, and do the work afterwards. With header authentication a timeout isn't retried, so a slow handler loses events. See why to process webhooks asynchronously.
Fetch the ticket rather than trusting the template
Treat the request as a prompt. Use {{ticket.id}} to fetch the current ticket from the Gorgias API, so your data is right even if requests arrive out of order.
Make handlers idempotent
With no delivery ID, a retried request after an unreachable endpoint looks like a new one. Key your writes on the ticket ID and the state you're applying. See our guide to webhook idempotency.
Choose JSON
Use application/json for non-GET methods. A GET integration can only pass data in the URL, which also ends up in access logs.
Conclusion
Gorgias HTTP integrations send a templated request for the ticket triggers you choose, authenticated by a header or OAuth2 rather than a signature. Use one integration per trigger with the event name in the body, compare the header in constant time, answer within 5 seconds, and don't count on retries: Gorgias never retries error responses, and with header authentication it doesn't retry timeouts either.
Hookdeck Event Gateway checks each Gorgias request, stores it, and retries into your service on your schedule.
Get started with Hookdeck for free and handle Gorgias webhooks reliably in minutes.