# How to Receive Webhooks with GCP Pub/Sub

In this guide, we'll show you how to receive webhooks with Google Cloud Pub/Sub via Hookdeck. We'll walk you through setting up a Pub/Sub topic, creating a subscription, creating a Hookdeck Connection to receive a webhook, transform the payload, and publish a payload to a topic in GCP Pub/Sub.

> This guide covers using short-lived access tokens with the GCP Pub/Sub API. By default, access tokens last up to an hour. With additional configuration and policy changes, they can have a maximum expiry of ninety (90) days. Hookdeck doesn't presently support [OAuth 2.0 for Server to Server Applications](https://developers.google.com/identity/protocols/oauth2/service-account#httprest). If you have a production use case for using Hookdeck with the GCP API, [get in touch](/contact).

## Before you begin

You'll need the following:

* A [Hookdeck account](https://dashboard.hookdeck.com/signup)
* A [Google Cloud Platform (GCP) account](https://cloud.google.com/)
* The [Google Cloud SDK](https://cloud.google.com/sdk/docs/install) installed on your machine. For Mac users, you can install it via Homebrew with `brew install --cask google-cloud-sdk`.

## Setup Google Cloud Pub/Sub

### Enabling Google Cloud Pub/Sub Using Google Cloud SDK (`gcloud`)

Begin by initializing the Google Cloud SDK with your Google Cloud account:

```bash
gcloud init

```

Next, enable the Pub/Sub API:

```bash
gcloud services enable pubsub.googleapis.com

```

### Verify the Enabled Service

Check if the Pub/Sub API is enabled:

```bash
gcloud services list --enabled

```

To check only for Pub/Sub:

```bash
gcloud services list --enabled | grep pubsub

```

### Create a Pub/Sub Topic

After enabling the API, create a topic:

```bash
gcloud pubsub topics create YOUR_TOPIC_NAME

```

Example:

```bash
gcloud pubsub topics create order.created

```

```bash
Created topic [projects/YOUR_PROJECT_ID/topics/order.created].

```

### Create a Pub/Sub Subscription

If you need a subscription to receive messages, create one:

```bash
gcloud pubsub subscriptions create YOUR_SUBSCRIPTION_NAME --topic=YOUR_TOPIC_NAME

```

Example:

```bash
gcloud pubsub subscriptions create orders --topic=order.created

```

You will see output similar to:

```bash
Created subscription [projects/YOUR_PROJECT_ID/subscriptions/orders].

```

### Publish a Test Message to Pub/Sub

You can publish a message directly using `gcloud`:

```bash
gcloud pubsub topics publish YOUR_TOPIC_NAME --message=YOUR_MESSAGE

```

Example:

```bash
gcloud pubsub topics publish order.created --message="{\"order_id\": \"1234\"}"

```

The output will be similar to:

```bash
messageIds:
- 'MESSAGE_ID'

```

### Pull Messages from a Subscription

If you're using a pull subscription, fetch messages with:

```bash
gcloud pubsub subscriptions pull YOUR_SUBSCRIPTION_NAME --auto-ack

```

Example:

```bash
gcloud pubsub subscriptions pull orders --auto-ack

```

You will see the message you published listed in a table format:

```
┌──────────────────────┬───────────────────┬──────────────┬────────────┬──────────────────┬────────────┐
│         DATA         │     MESSAGE_ID    │ ORDERING_KEY │ ATTRIBUTES │ DELIVERY_ATTEMPT │ ACK_STATUS │
├──────────────────────┼───────────────────┼──────────────┼────────────┼──────────────────┼────────────┤
│ {"order_id": "1234"} │ 13784194929806362 │              │            │                  │ SUCCESS    │
└──────────────────────┴───────────────────┴──────────────┴────────────┴──────────────────┴────────────┘

```

With this, GCP Pub/Sub is set up and ready to receive webhooks from Hookdeck.

### Get an Access Token to use the Pub/Sub API

To use the Pub/Sub API, you'll need an access token. You can get one using the `gcloud` command:

```bash
gcloud auth print-access-token

```

> Reminder: This access token is valid for one (1) hour.

Copy this access token for later.

We now have:

* A Pub/Sub topic to publish messages to
* A Pub/Sub subscription to receive messages
* An access token to use the Pub/Sub API

## Create a Hookdeck Connection

Go to the [Hookdeck dashboard](https://dashboard.hookdeck.com) and create a new Connection.

### Source Configuration

Select the source of your webhooks. If the specific platform isn't listed, you can use the Webhook source. Give the Source a name.

![Connection Source configuration](./images/gcp-pubsub/source.png)

For production use cases, ensure you enable and configure authentication for your Source.

### Destination Configuration

For the Destination, choose HTTP, enter a name for the destination such as `gcp-pubsub-TOPIC_NAME`, replace `TOPIC_NAME` with the name of the Pub/Sub topic you created. From the examples above, the name would be `gcp-pubsub-order-created`.

For the URL, use the following format:

```bash
https://pubsub.googleapis.com/v1/projects/YOUR_PROJECT_ID/topics/YOUR_TOPIC_NAME:publish

```

Replace `YOUR_PROJECT_ID` and `YOUR_TOPIC_NAME` with your GCP project ID and the Pub/Sub topic name.

Example:

```bash
https://pubsub.googleapis.com/v1/projects/ecomm-store/topics/order.created:publish 

```

Under Advanced Configuration, set the Authentication Method to `Bearer Token` and paste the access token you obtained earlier.

![Create a new Connection](./images/gcp-pubsub/destination.png)

### Transform Payload

The incoming webhook payload will not be in the format expected by GCP Pub/Sub. The required format is:

```json
{
  "messages": [
    {
      "data": "BASE64_ENCODED_PAYLOAD"
    }
  ]
}

```

You'll need to transform the payload to match the Pub/Sub message format.

Under Define your connection rules, click Transform and then Create new Transformation.

Copy and paste the following into the transformation editor:

```js
/**
 * Minified by jsDelivr using Terser v5.37.0.
 * Original file: /npm/buffer-polyfill@6.0.3/dist/buffer.js
 *
 * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
 */
var ur = {}, G = {}; G.byteLength = Lr, G.toByteArray = Mr, G.fromByteArray = br; for (var S = [], R = [], Sr = typeof Uint8Array < "u" ? Uint8Array : Array, H = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", D = 0, _r = H.length; D < _r; ++D)S[D] = H[D], R[H.charCodeAt(D)] = D; function hr(t) { var e = t.length; if (e % 4 > 0) throw new Error("Invalid string. Length must be a multiple of 4"); var r = t.indexOf("="); return -1 === r && (r = e), [r, r === e ? 0 : 4 - r % 4] } function Lr(t) { var e = hr(t), r = e[0], n = e[1]; return 3 * (r + n) / 4 - n } function Nr(t, e, r) { return 3 * (e + r) / 4 - r } function Mr(t) { var e, r, n = hr(t), o = n[0], i = n[1], f = new Sr(Nr(t, o, i)), s = 0, u = i > 0 ? o - 4 : o; for (r = 0; r < u; r += 4)e = R[t.charCodeAt(r)] << 18 | R[t.charCodeAt(r + 1)] << 12 | R[t.charCodeAt(r + 2)] << 6 | R[t.charCodeAt(r + 3)], f[s++] = e >> 16 & 255, f[s++] = e >> 8 & 255, f[s++] = 255 & e; return 2 === i && (e = R[t.charCodeAt(r)] << 2 | R[t.charCodeAt(r + 1)] >> 4, f[s++] = 255 & e), 1 === i && (e = R[t.charCodeAt(r)] << 10 | R[t.charCodeAt(r + 1)] << 4 | R[t.charCodeAt(r + 2)] >> 2, f[s++] = e >> 8 & 255, f[s++] = 255 & e), f } function kr(t) { return S[t >> 18 & 63] + S[t >> 12 & 63] + S[t >> 6 & 63] + S[63 & t] } function Dr(t, e, r) { for (var n, o = [], i = e; i < r; i += 3)n = (t[i] << 16 & 16711680) + (t[i + 1] << 8 & 65280) + (255 & t[i + 2]), o.push(kr(n)); return o.join("") } function br(t) { for (var e, r = t.length, n = r % 3, o = [], i = 16383, f = 0, s = r - n; f < s; f += i)o.push(Dr(t, f, f + i > s ? s : f + i)); return 1 === n ? (e = t[r - 1], o.push(S[e >> 2] + S[e << 4 & 63] + "==")) : 2 === n && (e = (t[r - 2] << 8) + t[r - 1], o.push(S[e >> 10] + S[e >> 4 & 63] + S[e << 2 & 63] + "=")), o.join("") } R["-".charCodeAt(0)] = 62, R["_".charCodeAt(0)] = 63; var V = {
  /*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
  read: function (t, e, r, n, o) { var i, f, s = 8 * o - n - 1, u = (1 << s) - 1, h = u >> 1, a = -7, c = r ? o - 1 : 0, p = r ? -1 : 1, l = t[e + c]; for (c += p, i = l & (1 << -a) - 1, l >>= -a, a += s; a > 0; i = 256 * i + t[e + c], c += p, a -= 8); for (f = i & (1 << -a) - 1, i >>= -a, a += n; a > 0; f = 256 * f + t[e + c], c += p, a -= 8); if (0 === i) i = 1 - h; else { if (i === u) return f ? NaN : 1 / 0 * (l ? -1 : 1); f += Math.pow(2, n), i -= h } return (l ? -1 : 1) * f * Math.pow(2, i - n) }, write: function (t, e, r, n, o, i) { var f, s, u, h = 8 * i - o - 1, a = (1 << h) - 1, c = a >> 1, p = 23 === o ? Math.pow(2, -24) - Math.pow(2, -77) : 0, l = n ? 0 : i - 1, y = n ? 1 : -1, g = e < 0 || 0 === e && 1 / e < 0 ? 1 : 0; for (e = Math.abs(e), isNaN(e) || e === 1 / 0 ? (s = isNaN(e) ? 1 : 0, f = a) : (f = Math.floor(Math.log(e) / Math.LN2), e * (u = Math.pow(2, -f)) < 1 && (f--, u *= 2), (e += f + c >= 1 ? p / u : p * Math.pow(2, 1 - c)) * u >= 2 && (f++, u /= 2), f + c >= a ? (s = 0, f = a) : f + c >= 1 ? (s = (e * u - 1) * Math.pow(2, o), f += c) : (s = e * Math.pow(2, c - 1) * Math.pow(2, o), f = 0)); o >= 8; t[r + l] = 255 & s, l += y, s /= 256, o -= 8); for (f = f << o | s, h += o; h > 0; t[r + l] = 255 & f, l += y, f /= 256, h -= 8); t[r + l - y] |= 128 * g }
};
/*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <https://feross.org>
 * @license  MIT
 */
!function (t) { const e = G, r = V, n = "function" == typeof Symbol && "function" == typeof Symbol.for ? Symbol.for("nodejs.util.inspect.custom") : null; t.Buffer = f, t.SlowBuffer = function (t) { return +t != t && (t = 0), f.alloc(+t) }, t.INSPECT_MAX_BYTES = 50; const o = 2147483647; function i(t) { if (t > o) throw new RangeError('The value "' + t + '" is invalid for option "size"'); const e = new Uint8Array(t); return Object.setPrototypeOf(e, f.prototype), e } function f(t, e, r) { if ("number" == typeof t) { if ("string" == typeof e) throw new TypeError('The "string" argument must be of type string. Received type number'); return h(t) } return s(t, e, r) } function s(t, e, r) { if ("string" == typeof t) return function (t, e) { if (("string" != typeof e || "" === e) && (e = "utf8"), !f.isEncoding(e)) throw new TypeError("Unknown encoding: " + e); const r = 0 | l(t, e); let n = i(r); const o = n.write(t, e); return o !== r && (n = n.slice(0, o)), n }(t, e); if (ArrayBuffer.isView(t)) return function (t) { if (X(t, Uint8Array)) { const e = new Uint8Array(t); return c(e.buffer, e.byteOffset, e.byteLength) } return a(t) }(t); if (null == t) throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type " + typeof t); if (X(t, ArrayBuffer) || t && X(t.buffer, ArrayBuffer) || typeof SharedArrayBuffer < "u" && (X(t, SharedArrayBuffer) || t && X(t.buffer, SharedArrayBuffer))) return c(t, e, r); if ("number" == typeof t) throw new TypeError('The "value" argument must not be of type number. Received type number'); const n = t.valueOf && t.valueOf(); if (null != n && n !== t) return f.from(n, e, r); const o = function (t) { if (f.isBuffer(t)) { const e = 0 | p(t.length), r = i(e); return 0 === r.length || t.copy(r, 0, 0, e), r } if (void 0 !== t.length) return "number" != typeof t.length || J(t.length) ? i(0) : a(t); if ("Buffer" === t.type && Array.isArray(t.data)) return a(t.data) }(t); if (o) return o; if (typeof Symbol < "u" && null != Symbol.toPrimitive && "function" == typeof t[Symbol.toPrimitive]) return f.from(t[Symbol.toPrimitive]("string"), e, r); throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type " + typeof t) } function u(t) { if ("number" != typeof t) throw new TypeError('"size" argument must be of type number'); if (t < 0) throw new RangeError('The value "' + t + '" is invalid for option "size"') } function h(t) { return u(t), i(t < 0 ? 0 : 0 | p(t)) } function a(t) { const e = t.length < 0 ? 0 : 0 | p(t.length), r = i(e); for (let n = 0; n < e; n += 1)r[n] = 255 & t[n]; return r } function c(t, e, r) { if (e < 0 || t.byteLength < e) throw new RangeError('"offset" is outside of buffer bounds'); if (t.byteLength < e + (r || 0)) throw new RangeError('"length" is outside of buffer bounds'); let n; return n = void 0 === e && void 0 === r ? new Uint8Array(t) : void 0 === r ? new Uint8Array(t, e) : new Uint8Array(t, e, r), Object.setPrototypeOf(n, f.prototype), n } function p(t) { if (t >= o) throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x" + o.toString(16) + " bytes"); return 0 | t } function l(t, e) { if (f.isBuffer(t)) return t.length; if (ArrayBuffer.isView(t) || X(t, ArrayBuffer)) return t.byteLength; if ("string" != typeof t) throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type ' + typeof t); const r = t.length, n = arguments.length > 2 && !0 === arguments[2]; if (!n && 0 === r) return 0; let o = !1; for (; ;)switch (e) { case "ascii": case "latin1": case "binary": return r; case "utf8": case "utf-8": return H(t).length; case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return 2 * r; case "hex": return r >>> 1; case "base64": return q(t).length; default: if (o) return n ? -1 : H(t).length; e = ("" + e).toLowerCase(), o = !0 } } function y(t, e, r) { let n = !1; if ((void 0 === e || e < 0) && (e = 0), e > this.length || ((void 0 === r || r > this.length) && (r = this.length), r <= 0) || (r >>>= 0) <= (e >>>= 0)) return ""; for (t || (t = "utf8"); ;)switch (t) { case "hex": return T(this, e, r); case "utf8": case "utf-8": return U(this, e, r); case "ascii": return R(this, e, r); case "latin1": case "binary": return S(this, e, r); case "base64": return I(this, e, r); case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return O(this, e, r); default: if (n) throw new TypeError("Unknown encoding: " + t); t = (t + "").toLowerCase(), n = !0 } } function g(t, e, r) { const n = t[e]; t[e] = t[r], t[r] = n } function w(t, e, r, n, o) { if (0 === t.length) return -1; if ("string" == typeof r ? (n = r, r = 0) : r > 2147483647 ? r = 2147483647 : r < -2147483648 && (r = -2147483648), J(r = +r) && (r = o ? 0 : t.length - 1), r < 0 && (r = t.length + r), r >= t.length) { if (o) return -1; r = t.length - 1 } else if (r < 0) { if (!o) return -1; r = 0 } if ("string" == typeof e && (e = f.from(e, n)), f.isBuffer(e)) return 0 === e.length ? -1 : d(t, e, r, n, o); if ("number" == typeof e) return e &= 255, "function" == typeof Uint8Array.prototype.indexOf ? o ? Uint8Array.prototype.indexOf.call(t, e, r) : Uint8Array.prototype.lastIndexOf.call(t, e, r) : d(t, [e], r, n, o); throw new TypeError("val must be string, number or Buffer") } function d(t, e, r, n, o) { let i, f = 1, s = t.length, u = e.length; if (void 0 !== n && ("ucs2" === (n = String(n).toLowerCase()) || "ucs-2" === n || "utf16le" === n || "utf-16le" === n)) { if (t.length < 2 || e.length < 2) return -1; f = 2, s /= 2, u /= 2, r /= 2 } function h(t, e) { return 1 === f ? t[e] : t.readUInt16BE(e * f) } if (o) { let n = -1; for (i = r; i < s; i++)if (h(t, i) === h(e, -1 === n ? 0 : i - n)) { if (-1 === n && (n = i), i - n + 1 === u) return n * f } else -1 !== n && (i -= i - n), n = -1 } else for (r + u > s && (r = s - u), i = r; i >= 0; i--) { let r = !0; for (let n = 0; n < u; n++)if (h(t, i + n) !== h(e, n)) { r = !1; break } if (r) return i } return -1 } function b(t, e, r, n) { r = Number(r) || 0; const o = t.length - r; n ? (n = Number(n)) > o && (n = o) : n = o; const i = e.length; let f; for (n > i / 2 && (n = i / 2), f = 0; f < n; ++f) { const n = parseInt(e.substr(2 * f, 2), 16); if (J(n)) return f; t[r + f] = n } return f } function B(t, e, r, n) { return W(H(e, t.length - r), t, r, n) } function E(t, e, r, n) { return W(function (t) { const e = []; for (let r = 0; r < t.length; ++r)e.push(255 & t.charCodeAt(r)); return e }(e), t, r, n) } function m(t, e, r, n) { return W(q(e), t, r, n) } function A(t, e, r, n) { return W(function (t, e) { let r, n, o; const i = []; for (let f = 0; f < t.length && !((e -= 2) < 0); ++f)r = t.charCodeAt(f), n = r >> 8, o = r % 256, i.push(o), i.push(n); return i }(e, t.length - r), t, r, n) } function I(t, r, n) { return 0 === r && n === t.length ? e.fromByteArray(t) : e.fromByteArray(t.slice(r, n)) } function U(t, e, r) { r = Math.min(t.length, r); const n = []; let o = e; for (; o < r;) { const e = t[o]; let i = null, f = e > 239 ? 4 : e > 223 ? 3 : e > 191 ? 2 : 1; if (o + f <= r) { let r, n, s, u; switch (f) { case 1: e < 128 && (i = e); break; case 2: r = t[o + 1], 128 == (192 & r) && (u = (31 & e) << 6 | 63 & r, u > 127 && (i = u)); break; case 3: r = t[o + 1], n = t[o + 2], 128 == (192 & r) && 128 == (192 & n) && (u = (15 & e) << 12 | (63 & r) << 6 | 63 & n, u > 2047 && (u < 55296 || u > 57343) && (i = u)); break; case 4: r = t[o + 1], n = t[o + 2], s = t[o + 3], 128 == (192 & r) && 128 == (192 & n) && 128 == (192 & s) && (u = (15 & e) << 18 | (63 & r) << 12 | (63 & n) << 6 | 63 & s, u > 65535 && u < 1114112 && (i = u)) } } null === i ? (i = 65533, f = 1) : i > 65535 && (i -= 65536, n.push(i >>> 10 & 1023 | 55296), i = 56320 | 1023 & i), n.push(i), o += f } return function (t) { const e = t.length; if (e <= v) return String.fromCharCode.apply(String, t); let r = "", n = 0; for (; n < e;)r += String.fromCharCode.apply(String, t.slice(n, n += v)); return r }(n) } t.kMaxLength = o, f.TYPED_ARRAY_SUPPORT = function () { try { const t = new Uint8Array(1), e = { foo: function () { return 42 } }; return Object.setPrototypeOf(e, Uint8Array.prototype), Object.setPrototypeOf(t, e), 42 === t.foo() } catch { return !1 } }(), !f.TYPED_ARRAY_SUPPORT && typeof console < "u" && "function" == typeof console.error && console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support."), Object.defineProperty(f.prototype, "parent", { enumerable: !0, get: function () { if (f.isBuffer(this)) return this.buffer } }), Object.defineProperty(f.prototype, "offset", { enumerable: !0, get: function () { if (f.isBuffer(this)) return this.byteOffset } }), f.poolSize = 8192, f.from = function (t, e, r) { return s(t, e, r) }, Object.setPrototypeOf(f.prototype, Uint8Array.prototype), Object.setPrototypeOf(f, Uint8Array), f.alloc = function (t, e, r) { return function (t, e, r) { return u(t), t <= 0 ? i(t) : void 0 !== e ? "string" == typeof r ? i(t).fill(e, r) : i(t).fill(e) : i(t) }(t, e, r) }, f.allocUnsafe = function (t) { return h(t) }, f.allocUnsafeSlow = function (t) { return h(t) }, f.isBuffer = function (t) { return null != t && !0 === t._isBuffer && t !== f.prototype }, f.compare = function (t, e) { if (X(t, Uint8Array) && (t = f.from(t, t.offset, t.byteLength)), X(e, Uint8Array) && (e = f.from(e, e.offset, e.byteLength)), !f.isBuffer(t) || !f.isBuffer(e)) throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'); if (t === e) return 0; let r = t.length, n = e.length; for (let o = 0, i = Math.min(r, n); o < i; ++o)if (t[o] !== e[o]) { r = t[o], n = e[o]; break } return r < n ? -1 : n < r ? 1 : 0 }, f.isEncoding = function (t) { switch (String(t).toLowerCase()) { case "hex": case "utf8": case "utf-8": case "ascii": case "latin1": case "binary": case "base64": case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return !0; default: return !1 } }, f.concat = function (t, e) { if (!Array.isArray(t)) throw new TypeError('"list" argument must be an Array of Buffers'); if (0 === t.length) return f.alloc(0); let r; if (void 0 === e) for (e = 0, r = 0; r < t.length; ++r)e += t[r].length; const n = f.allocUnsafe(e); let o = 0; for (r = 0; r < t.length; ++r) { let e = t[r]; if (X(e, Uint8Array)) o + e.length > n.length ? (f.isBuffer(e) || (e = f.from(e)), e.copy(n, o)) : Uint8Array.prototype.set.call(n, e, o); else { if (!f.isBuffer(e)) throw new TypeError('"list" argument must be an Array of Buffers'); e.copy(n, o) } o += e.length } return n }, f.byteLength = l, f.prototype._isBuffer = !0, f.prototype.swap16 = function () { const t = this.length; if (t % 2 != 0) throw new RangeError("Buffer size must be a multiple of 16-bits"); for (let e = 0; e < t; e += 2)g(this, e, e + 1); return this }, f.prototype.swap32 = function () { const t = this.length; if (t % 4 != 0) throw new RangeError("Buffer size must be a multiple of 32-bits"); for (let e = 0; e < t; e += 4)g(this, e, e + 3), g(this, e + 1, e + 2); return this }, f.prototype.swap64 = function () { const t = this.length; if (t % 8 != 0) throw new RangeError("Buffer size must be a multiple of 64-bits"); for (let e = 0; e < t; e += 8)g(this, e, e + 7), g(this, e + 1, e + 6), g(this, e + 2, e + 5), g(this, e + 3, e + 4); return this }, f.prototype.toString = function () { const t = this.length; return 0 === t ? "" : 0 === arguments.length ? U(this, 0, t) : y.apply(this, arguments) }, f.prototype.toLocaleString = f.prototype.toString, f.prototype.equals = function (t) { if (!f.isBuffer(t)) throw new TypeError("Argument must be a Buffer"); return this === t || 0 === f.compare(this, t) }, f.prototype.inspect = function () { let e = ""; const r = t.INSPECT_MAX_BYTES; return e = this.toString("hex", 0, r).replace(/(.{2})/g, "$1 ").trim(), this.length > r && (e += " ... "), "<Buffer " + e + ">" }, n && (f.prototype[n] = f.prototype.inspect), f.prototype.compare = function (t, e, r, n, o) { if (X(t, Uint8Array) && (t = f.from(t, t.offset, t.byteLength)), !f.isBuffer(t)) throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type ' + typeof t); if (void 0 === e && (e = 0), void 0 === r && (r = t ? t.length : 0), void 0 === n && (n = 0), void 0 === o && (o = this.length), e < 0 || r > t.length || n < 0 || o > this.length) throw new RangeError("out of range index"); if (n >= o && e >= r) return 0; if (n >= o) return -1; if (e >= r) return 1; if (this === t) return 0; let i = (o >>>= 0) - (n >>>= 0), s = (r >>>= 0) - (e >>>= 0); const u = Math.min(i, s), h = this.slice(n, o), a = t.slice(e, r); for (let t = 0; t < u; ++t)if (h[t] !== a[t]) { i = h[t], s = a[t]; break } return i < s ? -1 : s < i ? 1 : 0 }, f.prototype.includes = function (t, e, r) { return -1 !== this.indexOf(t, e, r) }, f.prototype.indexOf = function (t, e, r) { return w(this, t, e, r, !0) }, f.prototype.lastIndexOf = function (t, e, r) { return w(this, t, e, r, !1) }, f.prototype.write = function (t, e, r, n) { if (void 0 === e) n = "utf8", r = this.length, e = 0; else if (void 0 === r && "string" == typeof e) n = e, r = this.length, e = 0; else { if (!isFinite(e)) throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported"); e >>>= 0, isFinite(r) ? (r >>>= 0, void 0 === n && (n = "utf8")) : (n = r, r = void 0) } const o = this.length - e; if ((void 0 === r || r > o) && (r = o), t.length > 0 && (r < 0 || e < 0) || e > this.length) throw new RangeError("Attempt to write outside buffer bounds"); n || (n = "utf8"); let i = !1; for (; ;)switch (n) { case "hex": return b(this, t, e, r); case "utf8": case "utf-8": return B(this, t, e, r); case "ascii": case "latin1": case "binary": return E(this, t, e, r); case "base64": return m(this, t, e, r); case "ucs2": case "ucs-2": case "utf16le": case "utf-16le": return A(this, t, e, r); default: if (i) throw new TypeError("Unknown encoding: " + n); n = ("" + n).toLowerCase(), i = !0 } }, f.prototype.toJSON = function () { return { type: "Buffer", data: Array.prototype.slice.call(this._arr || this, 0) } }; const v = 4096; function R(t, e, r) { let n = ""; r = Math.min(t.length, r); for (let o = e; o < r; ++o)n += String.fromCharCode(127 & t[o]); return n } function S(t, e, r) { let n = ""; r = Math.min(t.length, r); for (let o = e; o < r; ++o)n += String.fromCharCode(t[o]); return n } function T(t, e, r) { const n = t.length; (!e || e < 0) && (e = 0), (!r || r < 0 || r > n) && (r = n); let o = ""; for (let n = e; n < r; ++n)o += Z[t[n]]; return o } function O(t, e, r) { const n = t.slice(e, r); let o = ""; for (let t = 0; t < n.length - 1; t += 2)o += String.fromCharCode(n[t] + 256 * n[t + 1]); return o } function L(t, e, r) { if (t % 1 != 0 || t < 0) throw new RangeError("offset is not uint"); if (t + e > r) throw new RangeError("Trying to access beyond buffer length") } function _(t, e, r, n, o, i) { if (!f.isBuffer(t)) throw new TypeError('"buffer" argument must be a Buffer instance'); if (e > o || e < i) throw new RangeError('"value" argument is out of bounds'); if (r + n > t.length) throw new RangeError("Index out of range") } function C(t, e, r, n, o) { j(e, n, o, t, r, 7); let i = Number(e & BigInt(4294967295)); t[r++] = i, i >>= 8, t[r++] = i, i >>= 8, t[r++] = i, i >>= 8, t[r++] = i; let f = Number(e >> BigInt(32) & BigInt(4294967295)); return t[r++] = f, f >>= 8, t[r++] = f, f >>= 8, t[r++] = f, f >>= 8, t[r++] = f, r } function x(t, e, r, n, o) { j(e, n, o, t, r, 7); let i = Number(e & BigInt(4294967295)); t[r + 7] = i, i >>= 8, t[r + 6] = i, i >>= 8, t[r + 5] = i, i >>= 8, t[r + 4] = i; let f = Number(e >> BigInt(32) & BigInt(4294967295)); return t[r + 3] = f, f >>= 8, t[r + 2] = f, f >>= 8, t[r + 1] = f, f >>= 8, t[r] = f, r + 8 } function M(t, e, r, n, o, i) { if (r + n > t.length) throw new RangeError("Index out of range"); if (r < 0) throw new RangeError("Index out of range") } function $(t, e, n, o, i) { return e = +e, n >>>= 0, i || M(t, 0, n, 4), r.write(t, e, n, o, 23, 4), n + 4 } function N(t, e, n, o, i) { return e = +e, n >>>= 0, i || M(t, 0, n, 8), r.write(t, e, n, o, 52, 8), n + 8 } f.prototype.slice = function (t, e) { const r = this.length; (t = ~~t) < 0 ? (t += r) < 0 && (t = 0) : t > r && (t = r), (e = void 0 === e ? r : ~~e) < 0 ? (e += r) < 0 && (e = 0) : e > r && (e = r), e < t && (e = t); const n = this.subarray(t, e); return Object.setPrototypeOf(n, f.prototype), n }, f.prototype.readUintLE = f.prototype.readUIntLE = function (t, e, r) { t >>>= 0, e >>>= 0, r || L(t, e, this.length); let n = this[t], o = 1, i = 0; for (; ++i < e && (o *= 256);)n += this[t + i] * o; return n }, f.prototype.readUintBE = f.prototype.readUIntBE = function (t, e, r) { t >>>= 0, e >>>= 0, r || L(t, e, this.length); let n = this[t + --e], o = 1; for (; e > 0 && (o *= 256);)n += this[t + --e] * o; return n }, f.prototype.readUint8 = f.prototype.readUInt8 = function (t, e) { return t >>>= 0, e || L(t, 1, this.length), this[t] }, f.prototype.readUint16LE = f.prototype.readUInt16LE = function (t, e) { return t >>>= 0, e || L(t, 2, this.length), this[t] | this[t + 1] << 8 }, f.prototype.readUint16BE = f.prototype.readUInt16BE = function (t, e) { return t >>>= 0, e || L(t, 2, this.length), this[t] << 8 | this[t + 1] }, f.prototype.readUint32LE = f.prototype.readUInt32LE = function (t, e) { return t >>>= 0, e || L(t, 4, this.length), (this[t] | this[t + 1] << 8 | this[t + 2] << 16) + 16777216 * this[t + 3] }, f.prototype.readUint32BE = f.prototype.readUInt32BE = function (t, e) { return t >>>= 0, e || L(t, 4, this.length), 16777216 * this[t] + (this[t + 1] << 16 | this[t + 2] << 8 | this[t + 3]) }, f.prototype.readBigUInt64LE = K((function (t) { F(t >>>= 0, "offset"); const e = this[t], r = this[t + 7]; (void 0 === e || void 0 === r) && z(t, this.length - 8); const n = e + 256 * this[++t] + 65536 * this[++t] + this[++t] * 2 ** 24, o = this[++t] + 256 * this[++t] + 65536 * this[++t] + r * 2 ** 24; return BigInt(n) + (BigInt(o) << BigInt(32)) })), f.prototype.readBigUInt64BE = K((function (t) { F(t >>>= 0, "offset"); const e = this[t], r = this[t + 7]; (void 0 === e || void 0 === r) && z(t, this.length - 8); const n = e * 2 ** 24 + 65536 * this[++t] + 256 * this[++t] + this[++t], o = this[++t] * 2 ** 24 + 65536 * this[++t] + 256 * this[++t] + r; return (BigInt(n) << BigInt(32)) + BigInt(o) })), f.prototype.readIntLE = function (t, e, r) { t >>>= 0, e >>>= 0, r || L(t, e, this.length); let n = this[t], o = 1, i = 0; for (; ++i < e && (o *= 256);)n += this[t + i] * o; return o *= 128, n >= o && (n -= Math.pow(2, 8 * e)), n }, f.prototype.readIntBE = function (t, e, r) { t >>>= 0, e >>>= 0, r || L(t, e, this.length); let n = e, o = 1, i = this[t + --n]; for (; n > 0 && (o *= 256);)i += this[t + --n] * o; return o *= 128, i >= o && (i -= Math.pow(2, 8 * e)), i }, f.prototype.readInt8 = function (t, e) { return t >>>= 0, e || L(t, 1, this.length), 128 & this[t] ? -1 * (255 - this[t] + 1) : this[t] }, f.prototype.readInt16LE = function (t, e) { t >>>= 0, e || L(t, 2, this.length); const r = this[t] | this[t + 1] << 8; return 32768 & r ? 4294901760 | r : r }, f.prototype.readInt16BE = function (t, e) { t >>>= 0, e || L(t, 2, this.length); const r = this[t + 1] | this[t] << 8; return 32768 & r ? 4294901760 | r : r }, f.prototype.readInt32LE = function (t, e) { return t >>>= 0, e || L(t, 4, this.length), this[t] | this[t + 1] << 8 | this[t + 2] << 16 | this[t + 3] << 24 }, f.prototype.readInt32BE = function (t, e) { return t >>>= 0, e || L(t, 4, this.length), this[t] << 24 | this[t + 1] << 16 | this[t + 2] << 8 | this[t + 3] }, f.prototype.readBigInt64LE = K((function (t) { F(t >>>= 0, "offset"); const e = this[t], r = this[t + 7]; (void 0 === e || void 0 === r) && z(t, this.length - 8); const n = this[t + 4] + 256 * this[t + 5] + 65536 * this[t + 6] + (r << 24); return (BigInt(n) << BigInt(32)) + BigInt(e + 256 * this[++t] + 65536 * this[++t] + this[++t] * 2 ** 24) })), f.prototype.readBigInt64BE = K((function (t) { F(t >>>= 0, "offset"); const e = this[t], r = this[t + 7]; (void 0 === e || void 0 === r) && z(t, this.length - 8); const n = (e << 24) + 65536 * this[++t] + 256 * this[++t] + this[++t]; return (BigInt(n) << BigInt(32)) + BigInt(this[++t] * 2 ** 24 + 65536 * this[++t] + 256 * this[++t] + r) })), f.prototype.readFloatLE = function (t, e) { return t >>>= 0, e || L(t, 4, this.length), r.read(this, t, !0, 23, 4) }, f.prototype.readFloatBE = function (t, e) { return t >>>= 0, e || L(t, 4, this.length), r.read(this, t, !1, 23, 4) }, f.prototype.readDoubleLE = function (t, e) { return t >>>= 0, e || L(t, 8, this.length), r.read(this, t, !0, 52, 8) }, f.prototype.readDoubleBE = function (t, e) { return t >>>= 0, e || L(t, 8, this.length), r.read(this, t, !1, 52, 8) }, f.prototype.writeUintLE = f.prototype.writeUIntLE = function (t, e, r, n) { if (t = +t, e >>>= 0, r >>>= 0, !n) { _(this, t, e, r, Math.pow(2, 8 * r) - 1, 0) } let o = 1, i = 0; for (this[e] = 255 & t; ++i < r && (o *= 256);)this[e + i] = t / o & 255; return e + r }, f.prototype.writeUintBE = f.prototype.writeUIntBE = function (t, e, r, n) { if (t = +t, e >>>= 0, r >>>= 0, !n) { _(this, t, e, r, Math.pow(2, 8 * r) - 1, 0) } let o = r - 1, i = 1; for (this[e + o] = 255 & t; --o >= 0 && (i *= 256);)this[e + o] = t / i & 255; return e + r }, f.prototype.writeUint8 = f.prototype.writeUInt8 = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 1, 255, 0), this[e] = 255 & t, e + 1 }, f.prototype.writeUint16LE = f.prototype.writeUInt16LE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 2, 65535, 0), this[e] = 255 & t, this[e + 1] = t >>> 8, e + 2 }, f.prototype.writeUint16BE = f.prototype.writeUInt16BE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 2, 65535, 0), this[e] = t >>> 8, this[e + 1] = 255 & t, e + 2 }, f.prototype.writeUint32LE = f.prototype.writeUInt32LE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 4, 4294967295, 0), this[e + 3] = t >>> 24, this[e + 2] = t >>> 16, this[e + 1] = t >>> 8, this[e] = 255 & t, e + 4 }, f.prototype.writeUint32BE = f.prototype.writeUInt32BE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 4, 4294967295, 0), this[e] = t >>> 24, this[e + 1] = t >>> 16, this[e + 2] = t >>> 8, this[e + 3] = 255 & t, e + 4 }, f.prototype.writeBigUInt64LE = K((function (t, e = 0) { return C(this, t, e, BigInt(0), BigInt("0xffffffffffffffff")) })), f.prototype.writeBigUInt64BE = K((function (t, e = 0) { return x(this, t, e, BigInt(0), BigInt("0xffffffffffffffff")) })), f.prototype.writeIntLE = function (t, e, r, n) { if (t = +t, e >>>= 0, !n) { const n = Math.pow(2, 8 * r - 1); _(this, t, e, r, n - 1, -n) } let o = 0, i = 1, f = 0; for (this[e] = 255 & t; ++o < r && (i *= 256);)t < 0 && 0 === f && 0 !== this[e + o - 1] && (f = 1), this[e + o] = (t / i | 0) - f & 255; return e + r }, f.prototype.writeIntBE = function (t, e, r, n) { if (t = +t, e >>>= 0, !n) { const n = Math.pow(2, 8 * r - 1); _(this, t, e, r, n - 1, -n) } let o = r - 1, i = 1, f = 0; for (this[e + o] = 255 & t; --o >= 0 && (i *= 256);)t < 0 && 0 === f && 0 !== this[e + o + 1] && (f = 1), this[e + o] = (t / i | 0) - f & 255; return e + r }, f.prototype.writeInt8 = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 1, 127, -128), t < 0 && (t = 255 + t + 1), this[e] = 255 & t, e + 1 }, f.prototype.writeInt16LE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 2, 32767, -32768), this[e] = 255 & t, this[e + 1] = t >>> 8, e + 2 }, f.prototype.writeInt16BE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 2, 32767, -32768), this[e] = t >>> 8, this[e + 1] = 255 & t, e + 2 }, f.prototype.writeInt32LE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 4, 2147483647, -2147483648), this[e] = 255 & t, this[e + 1] = t >>> 8, this[e + 2] = t >>> 16, this[e + 3] = t >>> 24, e + 4 }, f.prototype.writeInt32BE = function (t, e, r) { return t = +t, e >>>= 0, r || _(this, t, e, 4, 2147483647, -2147483648), t < 0 && (t = 4294967295 + t + 1), this[e] = t >>> 24, this[e + 1] = t >>> 16, this[e + 2] = t >>> 8, this[e + 3] = 255 & t, e + 4 }, f.prototype.writeBigInt64LE = K((function (t, e = 0) { return C(this, t, e, -BigInt("0x8000000000000000"), BigInt("0x7fffffffffffffff")) })), f.prototype.writeBigInt64BE = K((function (t, e = 0) { return x(this, t, e, -BigInt("0x8000000000000000"), BigInt("0x7fffffffffffffff")) })), f.prototype.writeFloatLE = function (t, e, r) { return $(this, t, e, !0, r) }, f.prototype.writeFloatBE = function (t, e, r) { return $(this, t, e, !1, r) }, f.prototype.writeDoubleLE = function (t, e, r) { return N(this, t, e, !0, r) }, f.prototype.writeDoubleBE = function (t, e, r) { return N(this, t, e, !1, r) }, f.prototype.copy = function (t, e, r, n) { if (!f.isBuffer(t)) throw new TypeError("argument should be a Buffer"); if (r || (r = 0), !n && 0 !== n && (n = this.length), e >= t.length && (e = t.length), e || (e = 0), n > 0 && n < r && (n = r), n === r || 0 === t.length || 0 === this.length) return 0; if (e < 0) throw new RangeError("targetStart out of bounds"); if (r < 0 || r >= this.length) throw new RangeError("Index out of range"); if (n < 0) throw new RangeError("sourceEnd out of bounds"); n > this.length && (n = this.length), t.length - e < n - r && (n = t.length - e + r); const o = n - r; return this === t && "function" == typeof Uint8Array.prototype.copyWithin ? this.copyWithin(e, r, n) : Uint8Array.prototype.set.call(t, this.subarray(r, n), e), o }, f.prototype.fill = function (t, e, r, n) { if ("string" == typeof t) { if ("string" == typeof e ? (n = e, e = 0, r = this.length) : "string" == typeof r && (n = r, r = this.length), void 0 !== n && "string" != typeof n) throw new TypeError("encoding must be a string"); if ("string" == typeof n && !f.isEncoding(n)) throw new TypeError("Unknown encoding: " + n); if (1 === t.length) { const e = t.charCodeAt(0); ("utf8" === n && e < 128 || "latin1" === n) && (t = e) } } else "number" == typeof t ? t &= 255 : "boolean" == typeof t && (t = Number(t)); if (e < 0 || this.length < e || this.length < r) throw new RangeError("Out of range index"); if (r <= e) return this; let o; if (e >>>= 0, r = void 0 === r ? this.length : r >>> 0, t || (t = 0), "number" == typeof t) for (o = e; o < r; ++o)this[o] = t; else { const i = f.isBuffer(t) ? t : f.from(t, n), s = i.length; if (0 === s) throw new TypeError('The value "' + t + '" is invalid for argument "value"'); for (o = 0; o < r - e; ++o)this[o + e] = i[o % s] } return this }; const P = {}; function k(t, e, r) { P[t] = class extends r { constructor() { super(), Object.defineProperty(this, "message", { value: e.apply(this, arguments), writable: !0, configurable: !0 }), this.name = `${this.name} [${t}]`, this.stack, delete this.name } get code() { return t } set code(t) { Object.defineProperty(this, "code", { configurable: !0, enumerable: !0, value: t, writable: !0 }) } toString() { return `${this.name} [${t}]: ${this.message}` } } } function D(t) { let e = "", r = t.length; const n = "-" === t[0] ? 1 : 0; for (; r >= n + 4; r -= 3)e = `_${t.slice(r - 3, r)}${e}`; return `${t.slice(0, r)}${e}` } function j(t, e, r, n, o, i) { if (t > r || t < e) { const n = "bigint" == typeof e ? "n" : ""; let o; throw o = i > 3 ? 0 === e || e === BigInt(0) ? `>= 0${n} and < 2${n} ** ${8 * (i + 1)}${n}` : `>= -(2${n} ** ${8 * (i + 1) - 1}${n}) and < 2 ** ${8 * (i + 1) - 1}${n}` : `>= ${e}${n} and <= ${r}${n}`, new P.ERR_OUT_OF_RANGE("value", o, t) } !function (t, e, r) { F(e, "offset"), (void 0 === t[e] || void 0 === t[e + r]) && z(e, t.length - (r + 1)) }(n, o, i) } function F(t, e) { if ("number" != typeof t) throw new P.ERR_INVALID_ARG_TYPE(e, "number", t) } function z(t, e, r) { throw Math.floor(t) !== t ? (F(t, r), new P.ERR_OUT_OF_RANGE(r || "offset", "an integer", t)) : e < 0 ? new P.ERR_BUFFER_OUT_OF_BOUNDS : new P.ERR_OUT_OF_RANGE(r || "offset", `>= ${r ? 1 : 0} and <= ${e}`, t) } k("ERR_BUFFER_OUT_OF_BOUNDS", (function (t) { return t ? `${t} is outside of buffer bounds` : "Attempt to access memory outside buffer bounds" }), RangeError), k("ERR_INVALID_ARG_TYPE", (function (t, e) { return `The "${t}" argument must be of type number. Received type ${typeof e}` }), TypeError), k("ERR_OUT_OF_RANGE", (function (t, e, r) { let n = `The value of "${t}" is out of range.`, o = r; return Number.isInteger(r) && Math.abs(r) > 2 ** 32 ? o = D(String(r)) : "bigint" == typeof r && (o = String(r), (r > BigInt(2) ** BigInt(32) || r < -(BigInt(2) ** BigInt(32))) && (o = D(o)), o += "n"), n += ` It must be ${e}. Received ${o}`, n }), RangeError); const Y = /[^+/0-9A-Za-z-_]/g; function H(t, e) { let r; e = e || 1 / 0; const n = t.length; let o = null; const i = []; for (let f = 0; f < n; ++f) { if (r = t.charCodeAt(f), r > 55295 && r < 57344) { if (!o) { if (r > 56319) { (e -= 3) > -1 && i.push(239, 191, 189); continue } if (f + 1 === n) { (e -= 3) > -1 && i.push(239, 191, 189); continue } o = r; continue } if (r < 56320) { (e -= 3) > -1 && i.push(239, 191, 189), o = r; continue } r = 65536 + (o - 55296 << 10 | r - 56320) } else o && (e -= 3) > -1 && i.push(239, 191, 189); if (o = null, r < 128) { if ((e -= 1) < 0) break; i.push(r) } else if (r < 2048) { if ((e -= 2) < 0) break; i.push(r >> 6 | 192, 63 & r | 128) } else if (r < 65536) { if ((e -= 3) < 0) break; i.push(r >> 12 | 224, r >> 6 & 63 | 128, 63 & r | 128) } else { if (!(r < 1114112)) throw new Error("Invalid code point"); if ((e -= 4) < 0) break; i.push(r >> 18 | 240, r >> 12 & 63 | 128, r >> 6 & 63 | 128, 63 & r | 128) } } return i } function q(t) { return e.toByteArray(function (t) { if ((t = (t = t.split("=")[0]).trim().replace(Y, "")).length < 2) return ""; for (; t.length % 4 != 0;)t += "="; return t }(t)) } function W(t, e, r, n) { let o; for (o = 0; o < n && !(o + r >= e.length || o >= t.length); ++o)e[o + r] = t[o]; return o } function X(t, e) { return t instanceof e || null != t && null != t.constructor && null != t.constructor.name && t.constructor.name === e.name } function J(t) { return t != t } const Z = function () { const t = "0123456789abcdef", e = new Array(256); for (let r = 0; r < 16; ++r) { const n = 16 * r; for (let o = 0; o < 16; ++o)e[n + o] = t[r] + t[o] } return e }(); function K(t) { return typeof BigInt > "u" ? Q : t } function Q() { throw new Error("BigInt not supported") } }(ur); const Buffer = ur.Buffer;
//# sourceMappingURL=/sm/b1d4c0ce7b18f801987dc33ac902cfe01bdf90be5918e4a221f440da81c4f1e9.map

addHandler('transform', (request, context) => {
  const message_data = Buffer.from(JSON.stringify(request.body)).toString('base64');

  request.body = {
    "messages": [
      { "data": message_data }
    ]
  }

  return request;
});

```

The majority of this code provides a polyfill for the `Buffer` object that isn't natively supported within Hookdeck Transformations. This is used to convert the message payload to the required base64 format.

The `addHandler` function is the main entry point for the transformation. It converts the `request.body` into the format expected by the GCP Pub/Sub API.

```js
addHandler('transform', (request, context) => {
  const message_data = Buffer.from(JSON.stringify(request.body)).toString('base64');

  request.body = {
    "messages": [
      { "data": message_data }
    ]
  }

  return request;
});

```

To test the Transformation, add the following to the Input section on the left:

```json
{
  "headers": {
    "content-type": "application/json"
  },
  "body": {
    "order_id": 1234
  },
  "query": "?",
  "parsed_query": {}
}

```

Click Run to see the output. The output should look like this:

![Hookdeck Transformation Output](./images/gcp-pubsub/transformation.png)

The output now matches the expected structure for the GCP Pub/Sub API.

Click Confirm, name your Transformation `to-gcp-pubsub`, click Confirm and you'll return to the Connection creation page. There, you'll see the `to-gcp-pubsub` rule applied to your Connection.

![Hookdeck Connection Rules](./images/gcp-pubsub/connection-rules.png)

Click Save to save your Connection.

You'll be presented with a Connection Created dialog and a URL for the Source of the new Connection. Copy that URL.

In addition to GCP Pub/Sub being set up you now have:

* A Hookdeck Source that listens for incoming webhooks.
* A Hookdeck Transformation that converts the incoming webhook into a format that GCP Pub/Sub can understand.
* A Hookdeck Destination that sends the transformed webhook to the GCP Pub/Sub API.

## Send a Webhook to GCP Pub/Sub via Hookdeck

Make a request to your Hookdeck Source URL:

```bash
curl -X POST -H "Content-Type: application/json" -d '{"order_id": 1234}' YOUR_HOOKDECK_SOURCE_URL

```

You will see the request accepted by Hookdeck:

```bash
{"status":"SUCCESS","message":"Request handled by Hookdeck. Check your dashboard to inspect the request: https://dashboard.hookdeck.com/requests/REQUEST_ID","request_id":"REQUEST_ID"}%   

```

Test that this has been received by GCP Pub/Sub by running the following command:

```bash
gcloud pubsub subscriptions pull YOUR_SUBSCRIPTION_NAME --auto-ack

```

And the output will show you the message with the payload you've just sent via Hookdeck:

```
┌───────────────────┬───────────────────┬──────────────┬────────────┬──────────────────┬────────────┐
│        DATA       │     MESSAGE_ID    │ ORDERING_KEY │ ATTRIBUTES │ DELIVERY_ATTEMPT │ ACK_STATUS │
├───────────────────┼───────────────────┼──────────────┼────────────┼──────────────────┼────────────┤
│ {"order_id":1234} │ 14227719344785380 │              │            │                  │ SUCCESS    │
└───────────────────┴───────────────────┴──────────────┴────────────┴──────────────────┴────────────┘

```

You have successfully sent a webhook to GCP Pub/Sub via Hookdeck!

## Conclusion

In this guide, you learned how to send webhooks to GCP Pub/Sub using Hookdeck. You set up a Hookdeck Source to receive incoming webhooks, a Hookdeck Transformation to convert the incoming webhook into a format that GCP Pub/Sub can understand, and a Hookdeck Destination to send the transformed webhook to the GCP Pub/Sub API.

As mentioned, the access token used for the GCP Pub/Sub API Destination is short-lived, so the use case you have must align with that.

## Where next?

* [Learn more about Connecting Services](/docs/use-cases/third-party-routing)
* [Read up on Hookdeck Transformations](/docs/transformations)
* [See how Hookdeck Filters can help you route webhooks](/docs/filters)