Webhooks
To get subscription status updates, your POS can receive events through a webhook URL shared with seQura.
Supported event types
| Event type | When it fires |
|---|---|
subscriptions/created | Subscription created and confirmed by client |
subscriptions/activated | All subscription items have been activated |
subscriptions/cancelled | Subscription cancelled |
subscriptions/payment_status_changed | Payment succeeded or failed |
subscriptions/instalment_date_changed | Monthly charge day changed |
subscriptions/renting_plan_changed | Payment plan modified |
subscriptions/customer_updated | Customer personal data updated |
subscriptions/payment_method_updated | Payment method changed |
subscriptions/payment_success | Payment completed successfully |
subscriptions/payment_error | Payment failed to complete |
aml_documentation_required | AML check requires customer documents |
aml_documentation_validated | AML documentation approved |
order/account_in_debt | Part payment order is in debt |
order/account_up_to_date | Part payment order balance is up to date |
needs_card | Customer must confirm / update payment card |
denied | Fired when seQura's risk engine rejects the checkout application. |
cancelled | Fired when a checkout is cancelled before confirmation — by the operator, the customer, or a system timeout |
Webhook Delivery Behaviour
Request format
- Method: POST
- Content-Type: application/x-www-form-urlencoded
- User-Agent: SeQura-Event/1.0 (www.sequra.es)
- Timeout: 5 seconds per attempt
Expected response
The endpoint must return a 2xx status code to acknowledge receipt. No specific response body is required.
Return 501 to signal that a particular event type is not supported — SeQura will treat this as a successful delivery and will not retry.
Any other non-2xx status (4xx, 5xx) is treated as a failure and triggers a retry.
Redirects
SeQura follows up to 4 redirects (301, 302, 307, 308). Absolute and relative Location headers are both supported. Exceeding 4 hops causes the delivery to fail and retry.
Retries
Failed deliveries (non-2xx response, timeout, or connection error) are retried up to 10 times with exponential backoff.
After 10 exhausted retries the event is permanently dropped.
Delivery guarantees
- At-least-once — the same event may be delivered more than once (e.g. after a retry following a timeout where the endpoint had already processed the request). Processors should be idempotent: use event_id as a deduplication key.
- Events are delivered asynchronously — there is no guaranteed ordering between different event types for the same order.
Signature Verification
Every webhook delivery includes an X-SeQura-Signature header containing an HMAC-SHA256 signature of the raw request body, hex-encoded. Verify it to confirm the payload genuinely came from seQura and has not been tampered with.
HTTP header names are case-insensitivePer the HTTP specification (RFC 9110), header field names are case-insensitive. seQura may send this header as
X-SeQura-Signature,x-sequra-signature, or any other casing (for example, header names are lowercased over HTTP/2), so read it case-insensitively. Most frameworks normalize header names for you—e.g. PHP exposes it as$_SERVER['HTTP_X_SEQURA_SIGNATURE'].
Retrieve your signature_secret from GET /merchants/:merchant_ref/credentials Please note that this an endpoint on different domain (main checkout API)
expected = OpenSSL::HMAC.hexdigest("SHA256", signature_secret, request.raw_post)
ActiveSupport::SecurityUtils.secure_compare(expected, request.headers["X-SeQura-Signature"])import hmac
import hashlib
expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
hmac.compare_digest(expected, request.headers.get("X-SeQura-Signature", ""))$expected = hash_hmac('sha256', $rawBody, $secret);
hash_equals($expected, $_SERVER['HTTP_X_SEQURA_SIGNATURE'] ?? '');Always use a constant-time comparison function (
secure_compare,compare_digest,hash_equals) to prevent timing attacks.
Common attributes
| Attribute | Description |
|---|---|
| charset | UTF-8 |
| utf | √ - control character to check encoding is correct. |
| event | event type |
| event_id | unique event id |
| order_ref | UUID of subscription (generated by SeQura) |
| order_ref_1 | Subscription reference number provided by merchant |
| order_ref_2 | Optional second reference number |
Payload samples
subscriptions/created
Sent when subscription is fully confirmed by client. Includes masked payment method, contract number, confirmation time
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "subscriptions/created",
"event_id": "b9008195-8747-4697-9bda-ac19d56bb2c6",
"credit_card": "************2160",
"credit_card_type": "VISA",
"bank_account_last_digits": null,
"contract_number": "A681875191",
"confirmed_at": "2026-02-16T11:44:28.000+01:00"
}subscriptions/activated
Sent when subscription is activated (either all product delivered, or 30 days passed and subscription was activated automatically)
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "subscriptions/activated",
"event_id": "f2d641e7-9f46-41bd-875d-d45248c1d089",
"activated_at": "2026-02-16T11:44:28.000+01:00"
}subscriptions/cancelled
Sent when subscription is cancelled
{
"charset": "UTF-8",
"utf": "√",
"event": "subscriptions/cancelled",
"event_id": "1234",
"order_ref_1": "pos_1234",
"order_ref_2": "",
"order_ref": "060eef19-da6c-4a5b-9a43-8bf43cb63a2e",
"updated_at": "2025-07-03 12:26:24.093525003 CEST +02:00"
}subscriptions/updated
Sent when subscription is updated (cart changed on product exchanges)
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "subscriptions/updated",
"event_id": "3d201e87-e6ab-445d-8283-32a4f85c7138",
"updated_at": "2026-02-16T11:44:34.146+01:00"
}subscriptions/instalment_date_changed
subscriptions/instalment_date_changedSent when the monthly charge date is changed by seQura (by customer's request).
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "subscriptions/instalment_date_changed",
"event_id": "3b533849-1085-48c0-a767-14123a8da3b1",
"old_instalment_day": 5,
"new_instalment_day": 15,
"changed_on": "2026-04-02"
}| Field | Description |
|---|---|
old_instalment_day | Previous day of month for charges (1–28) |
new_instalment_day | New day of month for charges (1–28) |
changed_on | Date the change takes effect (ISO 8601) |
subscriptions/renting_plan_changed
subscriptions/renting_plan_changedSent when the subscription payment plan is modified.
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "subscriptions/renting_plan_changed",
"event_id": "d22cb7d1-7c3c-4960-a58e-732d1c575be0",
"next_charge_date": "2026-05-02",
"oldest_overdue_charge_date": null,
"current_order_value": "515.28"
}| Field | Description |
|---|---|
next_charge_date | Date of the next scheduled charge (ISO 8601) |
oldest_overdue_charge_date | Earliest unpaid charge date, if any (ISO 8601) |
current_order_value | Current outstanding order value |
subscriptions/customer_updated
subscriptions/customer_updatedSent when the customer's personal data has been updated in seQura.
{
"charset": "UTF-8",
"utf": "√",
"event": "subscriptions/customer_updated",
"event_id": "1234",
"order_ref_1": "pos_1234",
"order_ref_2": "",
"order_ref": "060eef19-da6c-4a5b-9a43-8bf43cb63a2e",
"given_names": "María",
"surnames": "García López",
"email": "[email protected]",
"phone": "+34666123456",
"date_of_birth": "1990-01-01",
"nin": "12345678A"
}All data fields are optional. Only updated fields are included.
subscriptions/payment_method_updated
subscriptions/payment_method_updatedSent when the customer's payment method changes.
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "subscriptions/payment_method_updated",
"event_id": "5264d1fe-479d-4843-804d-51bdcf8d0d34",
"credit_card": "************2160",
"credit_card_type": null
}| Field | Description |
|---|---|
credit_card | Last 4 digits of the new card (if card payment) |
credit_card_type | Card brand: VISA, MASTERCARD, etc. |
subscriptions/payment_status_changed
subscriptions/payment_status_changedSent when payment status changes
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "subscriptions/payment_status_changed",
"event_id": "102cc717-940d-40a6-9f97-bc59e89eea70",
"successful": true,
"updated_at": "2026-02-16T11:44:34.146+01:00"
}subscriptions/payment_success
subscriptions/payment_successFired whenever a payment against a subscription is successfully confirmed. The event fires once per confirmed payment and is emitted regardless of whether the order has outstanding debt — i.e. partial payments also produce one event each.
Payload
| Field | Description |
|---|---|
event | Always "subscriptions/payment_success" |
event_id | Unique id for this delivery, useful for idempotency |
amount | Amount of the confirmed payment, in the order's currency. Two decimal places (e.g. `49.90`) |
last_four_pan | Last four digits of the card used for the payment. null when no card reference is associated with the event |
card_brand | Card issuer name (e.g. "VISA", "MASTERCARD"). null when no card reference is associated with the event |
transaction_id | seQura's transaction reference for this payment |
paid_at | Timestamp when the payment was recorded |
Example
{
"event": "subscriptions/payment_success",
"event_id": "8f3a7c2e-4b1d-4e9a-9c5f-2a8b6d1e3f47",
"amount": 49.90,
"last_four_pan": "4242",
"card_brand": "VISA",
"transaction_id": "txn_9f2e1c0a8b",
"paid_at": "2026-06-29T10:15:32Z"
}subscriptions/payment_error
subscriptions/payment_errorFired when a subscription payment cannot be collected. There are two situations that trigger this event:
- Charge declined at gateway — a scheduled charge reached the payment gateway and was rejected (e.g. card declined, insufficient funds, expired card). Payload reflects the card that was attempted and the gateway transaction reference.
- Charge skipped, no valid payment instrument — the scheduled charge could not be attempted because the subscription has no active card and no active direct-debit mandate. In this case
card_brandis set to the sentinel value"no_card", andlast_four_panandtransaction_idarenull.
Consumers can distinguish the two cases from the card_brand field.
Payload
| Field | Description |
|---|---|
event | Always "subscriptions/payment_error" |
event_id | Unique id for this delivery, useful for idempotency |
amount | Amount that failed to be collected, in the order's currency. Two decimal places (e.g. 42.50). In the skip case, this is the amount that would have been charged |
last_four_pan | Last four digits of the card that was charged. null when no card was involved (direct-debit mandate case, or skip case) |
card_brand | Card issuer name (e.g. "VISA", "MASTERCARD"). null when no card was involved. The sentinel "no_card" indicates the skip case — the attempt did not reach the gateway because no valid payment instrument was available |
transaction_id | seQura's transaction reference for the failed attempt. null in the skip case, since no attempt was made |
failed_at | ISO8601 timestamp when the failure was recorded |
Example — gateway decline
{
"event": "subscriptions/payment_error",
"event_id": "8f3a7c2e-4b1d-4e9a-9c5f-2a8b6d1e3f47",
"amount": 42.50,
"last_four_pan": "4242",
"card_brand": "VISA",
"transaction_id": "txn_9f2e1c0a8b",
"failed_at": "2026-07-03T10:15:32Z"
}Example — skipped, no valid payment instrument
{
"event": "subscriptions/payment_error",
"event_id": "d1c4a9b7-2e5f-4a83-9c1d-7b6e8f2a3c15",
"amount": 30.00,
"last_four_pan": null,
"card_brand": "no_card",
"transaction_id": null,
"failed_at": "2026-07-03T10:15:32Z"
}aml_documentation_required
aml_documentation_requiredSent when seQura's AML process requires the customer to submit identity documentation. The subscription's aml_status becomes required.
{
"charset": "UTF-8",
"utf": "√",
"event": "aml_documentation_required",
"event_id": "1234",
"order_ref_1": "pos_1234",
"order_ref_2": "",
"order_ref": "060eef19-da6c-4a5b-9a43-8bf43cb63a2e",
}Recommended action: Inform the customer that they must provide documentation. seQura will contact them directly. Any changes in subscription will be rejected until AML process is completed.
aml_documentation_validated
aml_documentation_validatedSent when the customer's AML documentation has been reviewed and approved. The subscription's aml_status becomes validated.
{
"charset": "UTF-8",
"utf": "√",
"event": "aml_documentation_validated",
"event_id": "1234",
"order_ref_1": "pos_1234",
"order_ref_2": "",
"order_ref": "060eef19-da6c-4a5b-9a43-8bf43cb63a2e",
}Recommended action: No action required. The subscription resumes normal operation.
order/account_in_debt
order/account_in_debtSent when Part Payment order balance becomes negative (monthly charge is applied, but not yet paid)
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "order/account_in_debt",
"event_id": "761865cc-7c9f-4a1f-9306-c24a10ed4bfa",
"balance": "-23.76"
}order/account_up_to_date
order/account_up_to_dateSent when balance becomes 0 or positive (all due charges are paid)
{
"charset": "UTF-8",
"utf": "√",
"order_ref_1": "990093571",
"order_ref_2": null,
"order_ref": "3e88b4a9-58d6-4fcb-b347-52189e9c3952",
"event": "order/account_up_to_date",
"event_id": "4e789cb4-90d3-4d86-bcfb-d92d828b9d30",
"balance": "0"
}Pre-Confirmation Events
These events fire during the checkout process, before a subscription is confirmed. Subscribe to them the same way as any other event type.
Common fields
| Field | Type | Description |
|---|---|---|
event | string | Event type name |
order_ref | string | seQura order UUID |
order_ref_1 | string | Your primary order reference (if provided) |
order_ref_2 | string | Your secondary order reference (if provided) |
merchant_ref | string | Your merchant reference |
charset | string | Always "UTF-8" |
denied
deniedFired when seQura's risk engine rejects the checkout application.
| Field | Type | Description |
|---|---|---|
apm_offered | boolean | true if an alternative payment method was offered to the customer |
Example:
{
"event": "denied",
"order_ref": "8a3f1b2c-4d5e-...",
"order_ref_1": "POS-7821",
"order_ref_2": null,
"apm_offered": false,
"charset": "UTF-8"
}cancelled
cancelledFired when a checkout is cancelled before confirmation — by the operator, the customer, or a system timeout.
Distinct from
subscriptions/cancelled, which applies to an already-confirmed subscription. This event is only sent for orders that went through an on-hold phase, a screening assessment, or originated from an instore sales flow.
No additional fields beyond the common set.
Example:
{
"event": "cancelled",
"order_ref": "8a3f1b2c-4d5e-...",
"order_ref_1": "POS-7821",
"order_ref_2": null,
"charset": "UTF-8"
}needs_card
needs_cardSent during initial subscription confirmation process, or changes confirmation process when the customer must update or confirm their payment card (e.g. PSD2 strong authentication required). The subscription status becomes waiting_for_payment.
{
"charset": "UTF-8",
"utf": "√",
"event": "needs_card",
"order_ref_1": "pos_1234",
"order_ref_2": "",
"order_ref": "060eef19-da6c-4a5b-9a43-8bf43cb63a2e",
"given_names": "María",
"surnames": "García López",
"email": "[email protected]",
"mobile_phone": "+34666123456",
"nin": "12345678A",
"date_of_birth": "1990-01-01",
"address": "Calle Mayor 1",
"city": "Barcelona",
"postal_code": "08001"
}Updated 8 days ago