Skip to main content
Add a webhook endpoint to a store. Each store can have up to 20 webhooks across all channels.
POST /v1/actions/store/add-webhook
Authentication: API Key (owner or admin role required)

Request Body

FieldTypeRequiredDescription
storeIdstringYesStore ID (Short ID format STO_xxx)
channelstringYesOne of http, feishu, discord, telegram, slack
urlstringYesTarget webhook URL (HTTPS; merchant ensures URL matches the chosen channel)
eventsstring[]YesSubscribed event types — e.g. ["order.completed", "refund.succeeded"]. Empty array means no events fire to this webhook.
testModebooleanYestrue = fires for test transactions; false = fires for production
secretstring | nullNoChannel-specific credential (e.g. Telegram chat_id). Stored as opaque text.

Example Request

import { WaffoPancake } from "@waffo/pancake-ts";

const client = new WaffoPancake({
  merchantId: process.env.WAFFO_MERCHANT_ID!,
  privateKey: process.env.WAFFO_PRIVATE_KEY!,
});

// Standard HTTPS webhook (RSA-signed envelope)
const { webhook } = await client.webhooks.add({
  storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw",
  channel: "http",
  url: "https://example.com/webhooks/pancake",
  events: ["order.completed", "refund.succeeded"],
  testMode: false,
});

Success Response (200)

{
  "data": {
    "webhook": {
      "id": "11111111-2222-3333-4444-555555555555",
      "storeId": "uuid-of-store",
      "channel": "http",
      "url": "https://example.com/webhooks/pancake",
      "events": ["order.completed", "refund.succeeded"],
      "testMode": false,
      "secret": null,
      "createdAt": "2026-05-07T00:00:00.000Z",
      "updatedAt": "2026-05-07T00:00:00.000Z"
    }
  }
}
The returned webhook.id is a UUID, not a Short ID — webhook IDs are not in the IdPrefix scope. Pass it as-is to update-webhook and remove-webhook.

Response Fields

FieldTypeDescription
idstringWebhook UUID
storeIdstringOwning store UUID
channelstringWebhook channel (http, feishu, discord, telegram, or slack)
urlstringTarget webhook URL
eventsstring[]Subscribed event types
testModebooleantrue for test transactions, false for production
secretstring | nullChannel-specific credential (opaque text), null if unset
createdAtstringCreation timestamp (ISO 8601)
updatedAtstringLast update timestamp (ISO 8601)

Errors

Retry policy: Never retry 4xx — fix the request and resubmit. Retry 5xx with exponential backoff (start 5s, max 3 attempts).
Statuserrors[0].messageWhat it meansRecommended handling
400Missing required field: storeIdstoreId was not providedFix the request body, resubmit
400Expected format: STO_xxx, got "..."storeId Short ID could not be decodedFix the storeId format, resubmit
400testMode must be a booleantestMode is not a booleanPass a boolean value, resubmit
400Invalid channel: must be one of http, feishu, discord, telegram, slackchannel is not in the allowed listUse one of the allowed channels
400Invalid URL formaturl is not a valid URLFix the URL, resubmit
400events must be a string arrayevents is not an array of stringsPass a string array, resubmit
400secret must be a string or nullsecret is not string or nullPass string or null, resubmit
400Webhook limit reached (max 20 per store)Store already has 20 webhooksRemove an existing webhook first
401Missing merchantId in request contextAPI Key authentication did not resolve a merchantVerify API Key headers and signature
403Not authorized to manage webhooks for this storeMerchant is not owner or admin of the storeVerify the merchant’s role on this store
500Internal server errorUnexpected server-side failureRetry with exponential backoff (start 5s, max 3 attempts)