# Gorgias Webhooks

Gorgias is an e-commerce helpdesk. Its outbound webhooks are **HTTP
integrations** (Settings → Account → HTTP integration): when a ticket event
fires, Gorgias calls your URL with the method, headers and body template you
configured.

## When to Use This Skill

* How do I receive Gorgias webhooks for ticket events?
* How do I verify / authenticate a Gorgias HTTP integration request?
* How do I tell `ticket-created` from `ticket-updated` in my webhook handler?
* What should my Gorgias request body template look like?
* Why does my Gorgias integration keep timing out or get disabled?

## How Gorgias Differs From Signed Webhooks

1. **No signature.** Gorgias doesn't sign HTTP integration requests. There
  are two authentication options: **header-based** (you add a header such
  as `Authorization: Bearer <secret>`; Gorgias sends it as-is) or **OAuth2**
  (Gorgias fetches a token from *your* token URL with a client ID and
  secret, then sends it in a header or query string).
2. **You write the payload.** The body is a template you configure, filled
  with Gorgias variables like `{{ticket.id}}`. Gorgias adds no envelope,
  no event name and no delivery ID.
3. **The trigger isn't in the request.** One integration can listen to
  several triggers, and nothing in the request says which one fired. Create
  **one integration per trigger** and hard-code the name in the body
  (`"event": "ticket-created"`). The examples dispatch on that field.

## Verification (core)

Header-based auth: compare the whole header value with the value you
configured, in constant time.

```javascript
const crypto = require('crypto');

// Gorgias integration header: Authorization: Bearer <GORGIAS_WEBHOOK_SECRET>
function verifyGorgiasAuthorization(header, secret) {
  if (!header || !secret) return false;                 // fail closed
  const a = Buffer.from(header, 'utf8');                 // the WHOLE header value
  const b = Buffer.from(`Bearer ${secret}`, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

```

```python
import hmac

def verify_gorgias_authorization(header: str | None, secret: str) -> bool:
    if not header or not secret:
        return False
    return hmac.compare_digest(header.encode(), f"Bearer {secret}".encode())

```

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/gorgias-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/gorgias-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/gorgias-webhooks/examples/fastapi/).

There is no Gorgias SDK for this, so every example does the compare
directly. For a custom header (e.g. `X-Gorgias-Secret: <secret>`) or OAuth2,
see [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/gorgias-webhooks/references/verification.md).

## Triggers (Event Types)

These are the trigger keys from the Gorgias Integration API (the UI shows
them as "Ticket created", etc.). Use the same strings as your `event` value.

| Trigger | Fires when |
| --- | --- |
| `ticket-created` | A ticket is created |
| `ticket-updated` | A ticket is updated (including tag changes) |
| `ticket-message-created` | A new message is added to a ticket |
| `ticket-self-unsnoozed` | A ticket's snooze delay ends |
| `ticket-message-failed` | A message fails to send |
| `ticket-assignment-updated` | The ticket assignee changes (exclusive: can't be combined with other triggers) |
| `ticket-status-updated` | The ticket status changes (exclusive: can't be combined with other triggers) |
| `ticket-handed-over` | A ticket is handed over (API only; needs further configuration) |

Recommended body template (`application/json`, one integration per trigger):

```json
{
  "event": "ticket-created",
  "ticket_id": "{{ticket.id}}",
  "subject": "{{ticket.subject}}",
  "status": "{{ticket.status}}",
  "channel": "{{ticket.channel}}",
  "customer_email": "{{ticket.customer.email}}",
  "assignee_email": "{{ticket.assignee_user.email}}",
  "updated_datetime": "{{ticket.updated_datetime}}",
  "message_text": "{{ticket.messages[-1].body_text}}"
}

```

The examples accept the same keys as `application/x-www-form-urlencoded`,
and the `POST`, `PUT`, `PATCH` and `DELETE` methods. See
[references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/gorgias-webhooks/references/overview.md) for variables.

## Delivery Behavior

* **5-second timeout.** Respond fast; queue slow work.
* **Retries:** up to 3 times (10s, 20s, 40s) when the endpoint can't be
  reached. Timeouts are retried **only for OAuth2** integrations. Error
  responses (4xx/5xx) are **never** retried, in either mode.
* **Auto-disable** after 500 consecutive failures. A success resets the count.
* No delivery ID: dedupe on `event` + `ticket_id` + `updated_datetime`.

## Environment Variables

```bash
# The secret in your Gorgias integration header: Authorization: Bearer <secret>
GORGIAS_WEBHOOK_SECRET=your_shared_secret

```

With it unset, the examples return 500 instead of accepting unauthenticated
requests.

## Local Development

```bash
npx hookdeck-cli listen 3000 gorgias --path /webhooks/gorgias

```

No account required. The CLI gives you a public HTTPS URL to paste into the
Gorgias integration's URL field, plus a web UI for inspecting requests. Use
port `8000` for the FastAPI example.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/gorgias-webhooks/references/overview.md) — Triggers, template variables, payload design, delivery behavior
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/gorgias-webhooks/references/setup.md) — Creating the HTTP integration in Gorgias (UI and API)
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/gorgias-webhooks/references/verification.md) — Header auth, custom headers, OAuth2, debugging