Agent skill

Yoco Webhooks Skill

Receive and verify Yoco webhooks (South African payments). Use when setting up Yoco webhook handlers for the Yoco API or Checkout API, debugging Standard Webhooks signature verification (webhook-id, webhook-timestamp, webhook-signature, whsec_ secret), or handling events like payment.created, payment.refunded, order.completed, payment.succeeded, and refund.succeeded.

Install this skill

npx skills add hookdeck/webhook-skills --skill yoco-webhooks


Yoco (South African card machines, POS, and online payments) sends webhooks from two APIs: the Yoco API (payments, orders, catalogue) and the Checkout API (hosted checkout payments and refunds). Both sign deliveries with Standard Webhooks: HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{rawBody} with a whsec_ secret.

When to Use This Skill

  • How do I receive Yoco webhooks in Express, Next.js, or FastAPI?
  • How do I verify Yoco webhook signatures?
  • How do I handle payment.created, payment.refunded, or order.completed events?
  • How do I confirm a Checkout API payment with payment.succeeded?
  • Why is my Yoco webhook signature verification failing?
  • Where do I get the Yoco webhook secret?

Signature at a Glance

PropertyValue
Headerswebhook-id, webhook-timestamp, webhook-signature
AlgorithmHMAC-SHA256, base64-encoded
Signed content{webhook-id}.{webhook-timestamp}.{rawBody}
KeyStrip whsec_ from the secret, base64-decode the rest
Signature headerSpace-separated list of v1,<base64sig>; accept if any matches
Timestamp toleranceYoco recommends 3 minutes
SecretReturned once, in the create-subscription response

Verification (core)

Yoco recommends the Standard Webhooks library (standardwebhooks on npm and PyPI). It defaults to a 5-minute window, so check Yoco's 3 minutes first. Always pass the raw request body.

const { Webhook } = require('standardwebhooks');

function verifyYocoWebhook(rawBody, headers, secret) {
  const ts = Number(headers['webhook-timestamp']);
  if (!Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > 180) {
    throw new Error('Timestamp outside the 3-minute tolerance');
  }
  // Strips "whsec_", base64-decodes the key, HMACs "{id}.{ts}.{body}",
  // and compares each "v1," signature in constant time. Throws on mismatch.
  return new Webhook(secret).verify(rawBody, headers);
}
from standardwebhooks.webhooks import Webhook

if abs(int(time.time()) - int(headers["webhook-timestamp"])) > 180:
    raise ValueError("Timestamp outside the 3-minute tolerance")
event = Webhook(secret).verify(raw_body, headers)  # raises on mismatch -> return 400

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

A manual (library-free) implementation is in references/verification.md.

Common Event Types

Yoco API events carry event_type; Checkout API events carry type. Read event.event_type ?? event.type if one endpoint receives both.

EventAPITriggered When
payment.createdYoco APIA payment is created
payment.refundedYoco APIA payment is refunded
order.created / order.updatedYoco APIAn order is opened / changed
order.completed / order.cancelledYoco APIAn order is fully paid / cancelled
item.*, item_category.*Yoco APICatalogue items or categories are created, updated, or deleted
payment.succeeded / payment.failedCheckout APIA checkout payment succeeds / fails
refund.succeeded / refund.failedCheckout APIA refund succeeds / fails

Checkout API webhooks fire for every payment on the account. Match payload.metadata.checkoutId to your checkout before fulfilling. See references/overview.md for payloads.

Environment Variables

YOCO_WEBHOOK_SECRET=whsec_xxxxx   # From the create-subscription response (shown once)

Local Development

Receive webhooks locally with the Hookdeck CLI — no account required, one paste-and-run line:

npx hookdeck-cli listen 3000 yoco --path /webhooks/yoco

Register the printed URL as your subscription's notification_url (Yoco API) or url (Checkout API). With the Yoco API, POST /v1/webhooks/subscriptions/{id}/test sends a signed sample event.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →