Skip to content

Operate · Webhooks

Sync your system with each transaction outcome without polling

Qore Payments sends a POST with the event and the full transaction payload when a transaction is approved, declined or cancelled, when its session expires, and when a capture, refund, void or credit is decided. It is signed with your merchant private key and retried 3 times.

Expect duplicates. The same webhook can be delivered more than once, so make your handler idempotent.

1 Payment provider Approves or declines the transaction
2 Qore Payments Sends the event with a signed JWT
3 Your webhook endpoint Verifies the JWT and the SHA-512 digest
Qore Payments Your systems Third-party provider

Overview

The Transaction Processed Webhook is triggered when a transaction's outcome changes, for example when it is approved, declined or cancelled, or when its session expires.\ This webhook enables you to synchronize your system with the transaction outcome without polling.

When It Is Triggered

Qore Payments will send the Transaction Processed Webhook when:

  • A transaction is approved or declined by the payment provider.
  • A transaction is cancelled.
  • A session expires before the customer pays.
  • A capture, refund, void or credit is approved or declined.

You see only the latest attempt of an order: a decline that can still be retried may reach you up to 15 minutes later, or not at all once a retry has its own outcome (see Delivery and Retry Policy).

A request rejected as invalid (status INVALID) triggers no Transaction Processed event: the API answer is the only outcome. A transaction-svc:transaction:created event may already have been sent for it.

Supported Events

The Transaction Processed Webhook is triggered for the following events:

Event Value Description
transaction-svc:transaction:authorized Any transaction approved (general event)
transaction-svc:transaction:authorize:authorized Authorization transaction successful
transaction-svc:transaction:purchase:approved Purchase transaction approved
transaction-svc:transaction:authorize:declined Authorization declined
transaction-svc:transaction:purchase:declined Purchase declined
transaction-svc:transaction:authorize:pending Authorization pending (not sent today, see below)
transaction-svc:transaction:purchase:pending Purchase pending (not sent today, see below)
transaction-svc:transaction:pending General transaction pending (not sent today, see below)
transaction-svc:transaction:declined General transaction declined
transaction-svc:transaction:cancelled General transaction cancelled
transaction-svc:transaction:authorize:cancelled Authorization cancelled
transaction-svc:transaction:purchase:cancelled Purchase cancelled
transaction-svc:transaction:session-expired The session expired before the customer paid
transaction-svc:transaction:management:declined Management operation declined (also the only declined event of a CREDIT)
transaction-svc:transaction:management:approved Management operation approved (also the only approved event of a CREDIT)
transaction-svc:transaction:management:refund:declined Refund operation declined
transaction-svc:transaction:management:refund:approved Refund operation approved
transaction-svc:transaction:management:void:declined Void operation declined
transaction-svc:transaction:management:void:approved Void operation approved
transaction-svc:transaction:management:capture:declined Capture operation declined
transaction-svc:transaction:management:capture:approved Capture operation approved

A general event and a type-specific event fire for the same change. For example, an approved purchase fires both transaction-svc:transaction:authorized and transaction-svc:transaction:purchase:approved, and an approved refund fires both transaction-svc:transaction:management:approved and transaction-svc:transaction:management:refund:approved. Subscribe to the general events or to the type-specific ones: an endpoint subscribed to both receives two deliveries for one change. A CREDIT fires only the general transaction-svc:transaction:management:approved / transaction-svc:transaction:management:declined events, so subscribe to those if you use credits.

The three pending events can be subscribed to, but Qore Payments does not send them for real transactions today. A transaction that goes pending sends no webhook until it reaches a final status; you then receive that status's events (approved, declined, cancelled or session expired). Only a test delivery from the merchant portal sends a pending event.

transaction-svc:transaction:created, sent when a transaction is created, belongs to a separate event service. It carries the same payload and is signed the same way, and shows the transaction as it is when the webhook is built. For a Hosted Payment Page or Embedded Fields session, processing_code and response_message are usually null, because the customer hasn't paid yet. After a Direct API authorize, they already carry the outcome.

Request Details

Detail Value
HTTP Method POST
Content-Type application/json
Authorization Header Bearer <JWT>
Timeout 15 seconds per attempt
Retries 3, with exponential backoff (about 1, 2 and 4 seconds, each varying by up to 50%). A slow endpoint can receive more; see Delivery and Retry Policy
After the last retry No further attempts
Signature Validation JWT signed using Merchant Private Key

Headers

Header Description
Authorization Bearer token containing a signed JWT with SHA-512 digest validation
Content-Type application/json
Accept text/plain
X-Webhook-Test true, only on a test delivery sent from the portal

Example:

POST /your/webhook/endpoint HTTP/1.1
Host: your-server.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
Accept: text/plain

Payload Schema

Here’s the JSON structure you can expect:

{
  "event": "transaction-svc:transaction:authorized",
  "payload": {
    "id": "KlLQpgjqCmsmogHfNjuS",
    "merchant_id": "eyCnTugDzIMADoQLTUJF",
    "terminal_id": "eyCnTugDzIMADoQLTUJF",
    "order_id": "auDCeVomqaFvBLxStpuO",
    "payment_link_id": null,
    "reference": "oONQfOdgIF",
    "description": "kzCPRVsEbY",
    "currency": "EUR",
    "amount": 100,
    "transaction_type": "PURCHASE",
    "processing_code": "0000",
    "response_message": "TRX_STATUS_APPROVED",
    "status": "APPROVED",
    "metadata": {
      "response_code": "TRX_STATUS_APPROVED",
      "simulated_flow": "VERSIONING_FINGERPRINT_N_FRICTIONLESS",
      "additionalProp1": "string",
      "additionalProp2": "string"
    },
    "created_at": "2025-04-28T12:42:26.976811Z",
    "updated_at": "2025-04-28T12:42:27.144339Z",
    "approval_code": "bfzLph",
    "reference_number": "",
    "parent_id": null,
    "systan": null,
    "transaction_initiated_by": "CUSTOMER",
    "authentication_level": null,
    "payment_provider_response_code": null,
    "payment_provider_response_message": null,
    "normalized_amount": 100,
    "customer_id": null,
    "payment_method_method": "41111111-******-1111",
    "payment_method_hash": null,
    "payment_method_type": "card",
    "payment_method_brand": "VISA",
    "payment_method_masked": "41111111-******-1111",
    "payment_method_token": null,
    "bin_issuer_country": "US",
    "customer_first_name": "Carey",
    "customer_last_name": "Kuhn",
    "customer_address": "877 Don Centers",
    "customer_city": "Port Dalton",
    "customer_country": "Singapore",
    "customer_postal_code": "43000",
    "customer_email": "barrett.rolfson@hotmail.com",
    "customer_phone_number": "+1 985-502-6937 x8449",
    "authentication_status": "AUTHENTICATED",
    "payment_method_expiry": "2027-12-31",
    "number_of_instalments": null
  }
}

In the example, number_of_instalments is null: the field is null unless the cardholder chose instalments. See Instalments.

Field Descriptions

Field Type Description
event string One of transaction processed events
payload.id string Unique transaction identifier
payload.merchant_id string Merchant identifier
payload.terminal_id string Terminal identifier
payload.order_id string Order ID associated with transaction
payload.payment_link_id string Payment Link ID (nullable)
payload.reference string Merchant-provided reference
payload.description string Transaction description
payload.currency string Currency code (ISO 4217)
payload.amount number (integer) Amount in minor units (e.g., cents)
payload.transaction_type string Transaction type, uppercase (e.g., PURCHASE, AUTHORIZE, REFUND)
payload.processing_code string or null Normalized Qore Payments processing code, e.g. 0000 (see Handling declines). Usually null on transaction-svc:transaction:created for a session the customer hasn't paid yet
payload.response_message string or null The processing code's name, e.g. TRX_STATUS_APPROVED for 0000 or RISK_FAILED_FRAUD for 4101. If provider details are enabled for your account, it can carry the provider's own message instead
payload.status string Transaction status, uppercase (APPROVED, DECLINED, PENDING, CANCELED, SESSION_EXPIRED, etc.)
payload.metadata object Custom merchant metadata (optional)
payload.created_at string (ISO-8601 datetime) When the transaction was created
payload.updated_at string (ISO-8601 datetime) When the transaction was last updated
payload.payment_provider_response_code string or null null on a merchant, shop or terminal endpoint, unless provider details are enabled for your account
payload.payment_provider_response_message string or null null on a merchant, shop or terminal endpoint, unless provider details are enabled for your account or Qore Payments has set the endpoint to include the provider's message
payload.bin_issuer_country string or null Country of the card issuer, from the card BIN
payload.payment_method_expiry string (ISO-8601 date) or null Payment method expiry date (last day of expiry month, e.g., 2027-12-31)
payload.number_of_instalments integer or null Number of instalments the cardholder chose in the card form; null when the payment was taken in full. Always present. If a Hosted Payment Page session sets options.number_of_instalments, that value is reported when the cardholder picks none. See Instalments.

Fields without a value are sent as null, not left out. Don't branch on payment_provider_response_code or payment_provider_response_message: branch on status, and read why a transaction failed from processing_code.

JSON Schema

Here is the JSON Schema for validating the Transaction Processed Webhook payload (download and view):

transaction-processed-payload.json

New fields can be added to the payload, as number_of_instalments was. The schema accepts fields it doesn't list, so don't reject a webhook because it carries a field you don't know.

Important Notes

  • Always verify the JWT signature using the appropriate merchant public key.
  • Always validate that the SHA-512 digest of the request body matches the value inside the JWT.
  • Qore Payments retries a failed delivery 3 times with exponential backoff, then drops the event. See Delivery and Retry Policy.
  • Duplicate webhook deliveries are possible: ensure your webhook handlers are idempotent.