Skip to main content

Webhooks

Publica.la sends a signed JSON POST request to each of your endpoints when an event it subscribes to happens in your store.

They replace the deprecated legacy sale webhook. Both can run at the same time; its page lists what changes.

Events​

EventSent when
order.paidThe first approved sale payment of an order is recorded.
charge.paidA payment gateway charge is approved, renewals included.

Endpoints​

Store administrators manage endpoints in the dashboard, in Settings > Webhooks (/dashboard/settings#webhooks). Plan administrators cannot.

PropertyDescription
URLWhere events are sent. See the rules below.
NameOptional label.
EventsThe event types the endpoint receives.
EnabledWhen cleared, the endpoint is paused.
Signing key64 hexadecimal characters. Shown once, on creation and rotation.

A store can have up to 10 endpoints.

The URL must:

  • Use http or https. With http, events travel unencrypted.
  • Use port 80 or 443 if it sets a port.
  • Resolve only to public IP addresses. This is checked again on every delivery.

Redirects are not followed. Requests come from the addresses in Platform IP Addresses.

ActionEffect
EditChanging the URL resets the failure count and re-enables a disabled endpoint.
PauseNothing is delivered. Events that happen while paused are dropped, not queued.
Rotate signing keyThe previous key stops working immediately.
Send testSends a test delivery. Up to 5 per endpoint per minute.
Re-enableShown on an endpoint Publica.la disabled, with the last error. Also resumes a paused one.
DeleteImmediate and permanent.

Recent deliveries lists the last 50 delivery attempts, kept for 30 days, with status Delivered (a 2xx), Failed (retried while attempts remain) or Skipped (not sent: the endpoint was paused, disabled or deleted, or the event had already reached it).

Request​

Headers​

HeaderValue
Content-Typeapplication/json
User-Agentpublica.la webhooks (+https://publica.la)
X-Pla-Signaturet=<unix timestamp>,v1=<hex HMAC-SHA256>. See Signature.
X-Pla-EventEvent type, for example order.paid. Equal to type in the body.
X-Pla-DeliveryDelivery id, evt_ followed by a UUID. Equal to id in the body.
X-Pla-Idempotency-KeyKey of the fact the event announces, for example order.paid:order:1001. Equal to idempotency_key.

Envelope​

{
"id": "evt_7c1d4e92-3b6a-4f0e-8a15-d2e9b0c47f36",
"type": "order.paid",
"created_at": "2026-01-15T14:30:06Z",
"idempotency_key": "order.paid:order:1001",
"tenant": {
"id": "42",
"slug": "yourstore"
},
"data": {}
}
FieldTypeDescription
idstringDelivery id. Unique per endpoint: two endpoints get different ids for one event.
typestringEvent type.
created_atstringISO 8601 UTC time the delivery was queued.
idempotency_keystringStable key of the fact the event announces. Same for every endpoint and every retry.
tenantobjectYour store: id (string) and slug (string).
dataobjectEvent payload, described on each event's page.

The body, id and created_at do not change across retries.

Payload conventions​

  • Ids are strings.
  • Money amounts are integers in the currency's minor unit, in fields ending in _in_cents: 1349 is 13.49 USD, 11900 is 11,900 CLP.
  • Dates are ISO 8601 strings in UTC.
  • Every key is always present. A missing value is null.
  • The payload is not versioned. New keys and event types can be added at any time. Renaming, removing or retyping a key ships as a new event type.

Signature​

X-Pla-Signature is t=<timestamp>,v1=<signature>:

  • t: Unix timestamp in seconds when the request was signed. Each retry is signed again with a new t.
  • v1: lowercase hex HMAC-SHA256 of the string <t>.<raw body>, keyed with the signing key as is (not hex-decoded).

To verify a request:

  1. Split the header on ,, and each part on the first =.
  2. Reject it if t is more than 300 seconds away from your current time, in either direction.
  3. Compute the HMAC over the raw body bytes as received, not over re-encoded JSON.
  4. Compare it with v1 in constant time.

Test vector:

InputValue
Signing key0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
Body{"id":"evt_00000000-0000-4000-8000-000000000000","type":"order.paid"}
Headert=1767225600,v1=cfdb027237491eed8330ae6e8aa4a543fa01895ab694b18d1095f4fb22cf23bb

Delivery​

PropertyValue
SuccessAny 2xx status. The response body is ignored.
FailureAny other status, 3xx included; no response within 10 seconds; a connection error; a URL that no longer resolves only to public IP addresses.
AttemptsUp to 12 per delivery, over about 70 hours.
OrderNot guaranteed, across events or endpoints.
DuplicatesPossible: delivery is at least once.

Retries​

Wait after each failed attempt:

After attemptWait
11 minute
25 minutes
315 minutes
41 hour
53 hours
66 hours
7 to 1112 hours

A delivery whose 12 attempts fail is exhausted and not sent again.

Duplicates​

The same event can arrive more than once, for example when your endpoint processed a request but its response took longer than 10 seconds. Deduplicate on X-Pla-Idempotency-Key, not on X-Pla-Delivery, which differs per endpoint.

Automatic disabling​

Publica.la disables an endpoint when both are true:

  • Its last 5 deliveries were exhausted.
  • It has had no successful delivery in the last 72 hours, or since it was created if that was less than 72 hours ago.

A successful delivery resets the count. A disabled endpoint receives nothing: new events are dropped and pending retries are cancelled. No notice is sent; the dashboard shows it as Disabled until it is re-enabled.

Test deliveries​

Send test sends the example payload of the first event in the Events table the endpoint receives (or of the first event in the table if it receives none), with real headers and signature. Its ids are fictitious. Unlike a real delivery:

  • Its idempotency key is test: followed by a UUID.
  • It is attempted once.
  • It is sent even when the endpoint is paused or disabled.
  • It does not count towards automatic disabling.