Webhooks

Get notified when products, designs and projects change, and verify every delivery.

Overview

Webhook subscriptions notify your app when something changes in a store, so you do not have to poll the API. After you subscribe to a topic, Printcart sends an HTTP POST to your callback URL whenever a matching event occurs. Creating, listing and removing subscriptions is done with the /webhooks endpoints in the API reference.

Topics and events

One subscription covers one topic and one event.

TopicResource
productsProduct
sidesSide
designsDesign
templatesTemplate
projectsProject (order)
userUser

Events: POST, PUT, DELETE, POST BATCH, PUT BATCH and DELETE BATCH.

The subscription object

PropertyDescription
idUnique identifier of the subscription
callback_urlDestination URL that receives the POST request when the event occurs
topicTopic of the subscription
eventEvent that triggers the webhook
created_atWhen the subscription was created
updated_atWhen the subscription was last updated

Payload

The request body wraps the resource exactly as the API returned it for the triggering request. The outer data is added by the delivery job.

json
{
  "data": {
    "data": {
      "id": "4419934f-8e1b-4cf0-b432-01ef9258a812",
      "name": "project example",
      "status": "processing",
      "note": "project example note",
      "created_at": "2021-11-16T08:26:30.000000Z",
      "updated_at": "2021-11-16T08:26:30.000000Z"
    },
    "message": "Created successfully"
  }
}

Batch events carry a list of resources in the inner data. The shape of each resource matches the corresponding schema in the API reference.

Verify the signature

Every delivery carries a Signature header. Verify it before you trust the request.

  • Header: Signature
  • Algorithm: HMAC-SHA256 of the raw request body, hex encoded
  • Key: base64(sid:secret), the same string you put after Basic in an Authorization header

Compute the HMAC over the raw body, not over parsed JSON, and compare with a constant-time function.

js
import crypto from 'node:crypto'

export function isValidSignature(rawBody, signatureHeader, sid, secret) {
  const key = Buffer.from(`${sid}:${secret}`).toString('base64')
  const expected = crypto.createHmac('sha256', key).update(rawBody).digest('hex')

  return (
    expected.length === signatureHeader.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))
  )
}

Keep the store sid and secret on your server and never publish them.

Delivery behaviour

  • A failed delivery is retried up to 3 times with exponential backoff, and each attempt times out after 3 seconds. Respond quickly and process the event asynchronously.
  • There is no delivery log and no replay endpoint. If your receiver was down, reconcile by listing the affected resources through the API.
  • Webhooks are sent from the production environment only, so test your receiver against a production test store.