https://api.southpay.io/api/v2.
Authentication and scope
Reads requirewebhooks:read, writes require webhooks:write. Publishable keys are rejected.
Identifiers
Endpoint and delivery ids are bare UUIDs. There is nowe_ or del_ prefix. No resource in this API uses prefixed ids.
List endpoints
GET /webhook_endpoints. Scope webhooks:read.
Returns a bare array, newest first. No envelope, no pagination. Soft-deleted endpoints are excluded.
string
Filter by
active, disabled or error.delivery_success_rate is a percentage to one decimal over non-test deliveries, or null when there have been none. It appears on this view only.
Create an endpoint
POST /webhook_endpoints. Scope webhooks:write. Idempotency keys are not used here.
All fields go inside a required webhook_endpoint object.
string
required
Where deliveries are posted. Must resolve to a public address. SouthPay rejects private, loopback and link-local hosts at delivery time.
string[]
required
Event names to receive. Must be a subset of the event catalogue. Omitting it on a non-platform endpoint yields an empty array, which fails validation.
string
Free text for your own reference.
string
Marks this as an e-commerce plugin integration:
woocommerce, prestashop, shopify, magento or custom. Changes create behaviour. See platform integrations. Cannot be changed after create.string
The storefront URL the plugin is installed on. Cannot be changed by
PATCH.object
Arbitrary key-value pairs stored on the endpoint. Not sent in deliveries.
signing_secret is returned in full only by create and rotate. Store it now. Every other response gives you signing_secret_preview, the first 12 characters, which cannot verify a signature. Losing the secret means rotating it, which breaks in-flight deliveries.Response fields
string
required
Bare UUID.
string
required
Delivery destination.
string
required
active, disabled (you turned it off) or error (SouthPay turned it off after 10 failed attempts on a delivery).string[]
required
Exactly the events this endpoint receives.
string
Full plaintext secret,
whsec_ plus 64 hex characters. Present on create and rotate only.string
required
First 12 characters plus
..., for identifying which secret is in use.string | null
The plugin platform, or null for a hand-registered endpoint.
boolean
required
True when
platform is set. Platform integrations are limited to one per store per mode.string
required
Stored as
"2026-04-14". It is informational. No payload builder reads it, so changing endpoints does not change payload shape.string | null
When the endpoint last confirmed a delivery.
number | null
Percentage to one decimal. Present on the list view only.
Retrieve an endpoint
GET /webhook_endpoints/{id}. Scope webhooks:read.
Adds the last 25 deliveries and the full event catalogue. Note this view does not include delivery_success_rate.
Response 200
object[]
Last 25 in the current mode, newest first.
string[]
Every subscribable event name, as a convenience for building a settings UI.
Update an endpoint
PATCH /webhook_endpoints/{id}. Scope webhooks:write.
Accepts url, description, subscribed_events and metadata only. platform and platform_store_url are fixed at create time and are silently ignored here. Returns 200 with the endpoint, without a signing_secret.
Enable and disable
PATCH /webhook_endpoints/{id}/enable sets status to active and clears the disabled timestamp.
PATCH /webhook_endpoints/{id}/disable stops deliveries.
Both take no body and return 200 with the endpoint.
Delete an endpoint
DELETE /webhook_endpoints/{id}. Scope webhooks:write. Soft delete. Returns 204 with no body. The endpoint stops receiving deliveries and disappears from the list.
Rotate the signing secret
POST /webhook_endpoints/{id}/rotate_secret. Scope webhooks:write. No body. Returns 200 with the endpoint including the new full signing_secret.
Rotation is an immediate hard cutover. There is no overlap window and no second valid secret. Deliveries already signed with the old secret will fail verification at your end. Deploy the new secret before you rotate, or accept the retries.
Send a test delivery
POST /webhook_endpoints/{id}/send_test. Scope webhooks:write.
string
Event to simulate. Defaults to the endpoint’s first subscribed event, falling back to
payment_intent.completed. Must be a valid event name.Response 200
GET /webhook_endpoints/{id} and look for the delivery_id in recent_deliveries. Test deliveries are excluded from delivery_success_rate.
An unknown event_type returns 422 {"error": "Unknown event type"}.
Redeliver an event
POST /webhook_endpoints/{id}/redeliver. Scope webhooks:write.
string
required
The delivery to send again. Accepted in the JSON body or as a query parameter.
200 {"message": "Redelivery queued"}. A delivery that is not retryable returns 422 {"error": "Delivery is not retryable"}.
Platform integrations
Sendingplatform on create changes the semantics. Only one platform integration can exist per store per mode, so re-running a plugin’s connect flow does not fail. It adopts the existing endpoint:
- The response is
200, not201and not409. - Any of
url,description,platform_store_url,metadataandsubscribed_eventsyou re-sent are applied. Omitted fields keep their current values, and emptysubscribed_eventsfalls back to the platform’s default set. - The endpoint is re-enabled if it was disabled.
- The signing secret is always rotated. The new secret is in the response; the old one stops verifying immediately.
200 from create exactly like a 201, and always persist the returned signing_secret.
A 409 {"error": "An active woocommerce integration already exists for this store"} is still reachable if two registrations race the database index.
Event catalogue
23 events are subscribable.payment_intent.canceled is the opposite case: it is emitted when an agent cancels an intent, but it is not in the catalogue, so no endpoint can subscribe to it and it is dropped. Detect agent cancellations by polling for status: "expired" with state_reason: "canceled_by_agent".

