Automations

Shopify to Social Media: Auto-Post New Products via Webhook

Subscribe to Shopify’s products/create webhook and publish a social post for every new product with PostGate’s API.

Quick answer

Auto-post new Shopify products by subscribing to the products/create webhook (via Settings → Notifications or the webhookSubscriptionCreate GraphQL mutation), pointed at a small relay endpoint. The relay verifies Shopify's X-Shopify-Hmac-Sha256 signature, maps the product payload to POST /api/posts, and uses the Shopify product ID as the Idempotency-Key so Shopify's retries never double-post.

Shopify has a real products/create webhook topic that fires the moment a new product is added — there's no PostGate app in the Shopify App Store to receive it, but the topic itself and the webhook mechanism are exactly what you'd expect from Shopify's own documentation. This guide sets up the subscription and a small relay endpoint that verifies the webhook and calls PostGate.

01

Why do you need a small endpoint in the middle?

Shopify webhooks always POST Shopify's own JSON payload to a URL you configure, signed with an X-Shopify-Hmac-Sha256 header — there's no way to add a custom Authorization header or reshape the body from Shopify's side. You need one endpoint that receives Shopify's payload, verifies it's genuinely from Shopify, and then makes its own call to PostGate with your API key attached.

02

Subscribe to products/create

For a quick setup on your own store, go to Settings → Notifications in the Shopify admin, scroll to Webhooks, and create one for the Product creation event, JSON format, pointed at your relay endpoint's URL. For a programmatic or app-based setup, the GraphQL Admin API's webhookSubscriptionCreate mutation does the same thing:

webhookSubscriptionCreatePostGate API
mutation {
  webhookSubscriptionCreate(
    topic: PRODUCTS_CREATE
    webhookSubscription: {
      uri: "https://relay.example.com/shopify/products-create"
      format: JSON
    }
  ) {
    webhookSubscription { id topic }
    userErrors { field message }
  }
}
03

Verify the signature, then relay to PostGate

Every Shopify webhook carries an HMAC-SHA256 signature of the raw request body, computed with the signing secret shown when you created the webhook. Recompute it and compare with a timing-safe check before trusting the payload — this is the step that stops anyone from POSTing a fake product event straight at your relay. Once verified, map Shopify's fields to PostGate's request shape and publish, using the Shopify product ID as the Idempotency-Key so a Shopify retry (Shopify itself retries failed deliveries) never posts twice.

Node.js / Express relay handlerPostGate API
import crypto from 'node:crypto';

app.post(
  '/shopify/products-create',
  express.raw({ type: 'application/json' }),
  async (req, res) => {
    const digest = crypto
      .createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SECRET)
      .update(req.body)
      .digest('base64');

    const signature = req.get('X-Shopify-Hmac-Sha256');
    if (
      !signature ||
      !crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature))
    ) {
      return res.status(401).end();
    }

    const product = JSON.parse(req.body.toString('utf8'));
    const shopDomain = req.get('X-Shopify-Shop-Domain');
    const body = `New: ${product.title}\n\nhttps://${shopDomain}/products/${product.handle}`;
    const imageUrl = product.images?.[0]?.src;

    await fetch(`https://postgate.studio/api/posts?profile_group_id=grp_acme123`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.POSTGATE_API_KEY}`,
        'Content-Type': 'application/json',
        'Idempotency-Key': `shopify-product-${product.id}`,
      },
      body: JSON.stringify({
        post: { body },
        profiles: ['pro_instagram123', 'pro_facebook456'],
        media: imageUrl ? [imageUrl] : undefined,
      }),
    });

    res.status(200).end();
  }
);
04

Test with a sandbox key before switching to live

Shopify's webhook settings include a Send test notification option that fires a sample payload with placeholder data — use it to confirm your relay parses the shape correctly before a real product ever triggers it. Point the relay at a pg_test_ key first: PostGate runs the entire pipeline against its sandbox adapter, so you can watch a full request succeed without actually posting to a connected account, then switch to pg_live_ once it's confirmed.

Frequently asked questions

Why can't a Shopify webhook call PostGate directly?

Shopify webhooks always POST Shopify's own JSON payload to a URL you configure, signed with an X-Shopify-Hmac-Sha256 header — there is no way to add a custom Authorization header or reshape the body from Shopify's side. You need one endpoint that receives Shopify's payload, verifies it, and then makes its own call to PostGate with your API key attached.

How do I verify a Shopify webhook is genuine?

Every Shopify webhook carries an HMAC-SHA256 signature of the raw request body, computed with the signing secret shown when you created the webhook. Recompute it and compare with a timing-safe check before trusting the payload — this is the step that stops anyone from POSTing a fake product event straight at your relay.

What stops a Shopify webhook retry from posting the product twice?

Shopify itself retries failed deliveries, so the relay uses the Shopify product ID as the Idempotency-Key on the PostGate call. A redelivered webhook maps onto the same logical post instead of publishing a second copy.

How do I test the Shopify relay before going live?

Shopify's webhook settings include a Send test notification option that fires a sample payload with placeholder data — use it to confirm your relay parses the shape correctly. Point the relay at a pg_test_ key first: PostGate runs the entire pipeline against its sandbox adapter without actually posting to a connected account, then switch to pg_live_ once confirmed.

Build it with PostGate

One API for every social platform your customers care about — sandboxed, documented, and ready today.

Start building Read the docs