External API

Developer documentation

Integrate online and POS payments, query operations, and receive webhooks with the Avirato Payments external API.

Webhooks

Webhooks are HTTP POST notifications that Avirato Payments sends automatically to your system when a relevant event occurs: a payment completes, a refund is processed, a capture is confirmed, etc.

Why they matter

Webhooks are the most reliable way to know that an operation has completed. Unlike the redirect (urlOk/urlKo), which depends on the customer returning to your page:

  • Webhooks arrive always, even if the customer closes the browser
  • They arrive a few seconds after the processor confirms the operation
  • They do not depend on user behavior

Recommendation: Use webhooks as the primary source for operation confirmation. Use redirect and polling as complements.

How to configure your webhook URL

From Integrations > Webhook Configuration in the dashboard you can register a public URL per environment (Live and Test). Each URL can be enabled/disabled separately. We recommend:

  • HTTPS URL with a valid certificate.
  • Endpoint dedicated to Avirato Payments (for example https://your-domain.com/avirato-payments/webhooks).
  • You need an active API key for the environment before configuring its URL. If none exists, the dashboard responds with 422.

Important: only webhooks generated from the moment the URL is configured and active are sent. Previous events are not resent retroactively.

Authenticity verification (HMAC signature)

All live webhooks arrive signed with HMAC-SHA256 using the same client_secret shown when you create (or rotate) the API key for the environment. Headers:

HeaderValue
X-Avirato-TimestampUnix timestamp (seconds) at send time
X-Avirato-Signaturet={timestamp},v1={hmac} — where hmac = hash_hmac('sha256', timestamp + "." + body, client_secret)

PHP verification example:

$timestamp = $_SERVER['HTTP_X_AVIRATO_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_AVIRATO_SIGNATURE'] ?? '';
$body = file_get_contents('php://input');

if (!preg_match('/^t=(\d+),v1=([a-f0-9]+)$/', $signature, $m)) {
    http_response_code(400);
    exit;
}

$expected = hash_hmac('sha256', $timestamp . '.' . $body, $clientSecret);
if (!hash_equals($expected, $m[2])) {
    http_response_code(401);
    exit;
}

In Test, if you have a test API key created, test webhooks are also signed with its secret. If not, they are sent without a signature so you can explore the format without creating a test API key yet.

Retry policy

How it works in one line

Each time Avirato Payments sends you a webhook and your endpoint does not respond with HTTP 200, it retries later. The wait between each failure and the next retry grows: it starts at 10 minutes and ends at 48 hours. After a total of 10 unsuccessful attempts Avirato Payments stops retrying automatically.

Full schedule

The table shows, for a webhook that fails on every attempt, the exact moment your endpoint receives the next call, counted from when the original event occurred (authorization, refund, etc.).

AttemptWhat it isWhen you receive it (from the event)
1Initial deliveryat the moment (≈ 0)
21st retry10 min later
32nd retry30 min later
43rd retry1 h 10 min later
54th retry2 h 40 min later
65th retry5 h 40 min later
76th retry11 h 40 min later
87th retry23 h 40 min later
98th retry1 day 23 h 40 min later
109th (and last) retry≈ 4 days later (95 h 40 min)
from here on it is no longer retried

There are 10 attempts in total (1 initial delivery + 9 retries) spread over about 4 days. The clock does NOT reset on each attempt: the later you respond correctly, the more spaced out the subsequent retries have been.

How to know which attempt we are on

In the dashboard, under Integrations > Live > Webhooks, each row in the history shows:

  • Attempts: number of attempts consumed so far (1 to 10).
  • Last attempt: date and time of the last delivery.
  • HTTP: HTTP code your endpoint returned on that last delivery.
  • Status: Delivered (success), Pending (retries still ahead), or Failed (not applicable in the live flow while retries remain).

What happens if the endpoint keeps failing

If after 10 attempts (≈ 4 days) your endpoint has not responded correctly even once, Avirato Payments stops retrying automatically even if you fix your system later. In that case:

  1. Reactivate your endpoint and verify it now responds correctly to normal webhooks.
  2. For events that remained pending, open Integrations > Live > Webhooks, identify webhooks with Pending status, and force manual resend from the "Resend" button. Each manual resend makes a new HTTP call to your endpoint with the same payload.

What happens if you temporarily disable or delete your URL

  • While your URL is disabled (or does not exist) Avirato Payments continues to queue webhooks for events that occur.
  • The cron still counts retries: if you reactivate the URL within the ≈ 4 day window, pending ones will be delivered automatically on the next tick (as long as attempts remain available for that specific webhook).
  • After that window, events are abandoned and are NOT sent again even if you reactivate the URL. You will need to request them manually from the dashboard if you need them.

Manual resend behavior

  • If you manually resend a webhook that was pending retries and the resend succeeds, automatic retries stop — the webhook is considered delivered.
  • You can resend a webhook that was already delivered successfully. The dashboard will ask for confirmation first to avoid accidental duplicates, but the resend still runs if you confirm.

Source of truth for payment status (Live)

In the Live environment, processor notifications have the final say on the real payment status. This means a payment can change status after your system has treated it as completed.

Typical cases where a later webhook may arrive that reverses a previous success:

  • Chargeback: the cardholder disputes the charge with their bank
  • Fraud detected later: the processor identifies a fraudulent transaction days later
  • Bank reversal: the issuing bank reverses the operation
  • Processor correction: a previous notification contained incomplete or incorrect data

When we receive one of these events in Live, we update the payment status in our system (for example, from AUTHORISED to FAILED) and send you the corresponding webhook. Your integration must:

  • Accept asynchronous webhooks that change the outcome of a previously successful payment
  • Treat webhooks (not the immediate response or redirect) as the reference for reconciliation and final status
  • Query the API again (GET /payment/session/{sessionId}) when you need the latest known status

Test vs Live: in Test webhooks are generated by our own simulator and reflect only the simulation you triggered. There are no chargebacks or reversals in Test.

General format

All webhooks arrive as POST to your URL with this format:

Headers

HeaderValue
Content-Typeapplication/json
webcodeWebcode associated with the payment (e.g. SHOP01)

Body

{
  "event_code": "AUTHORISATION",
  "data": {
    ...event-specific fields...
  }
}

Expected response

Your endpoint must respond with HTTP 200. The response body is not evaluated — the status code alone is enough.

If your endpoint does not respond with HTTP 200, the webhook will be retried automatically with exponential backoff.

---

Event types

AUTHORISATION — Payment completed (online or POS)

Sent when a payment (online or POS) is authorized successfully or fails.

{
  "event_code": "AUTHORISATION",
  "data": {
    "amount": 15000,
    "currency": "EUR",
    "paymentReference": "SHOP01-PXT-17195000005678",
    "paymentStatus": "AUTHORISED",
    "description": "Pedido #12345 - Servicio premium",
    "customReference": "ORD-2026-12345",
    "paymentSource": "external_api"
  }
}
FieldAlways presentDescription
amountYesAmount in cents
currencyYesISO 4217 code
paymentReferenceYesPayment reference
paymentStatusYesAUTHORISED if successful, FAILED if failed
descriptionYesPayment description
customReferenceNoYour internal reference (if provided when creating session/payment)
paymentSourceNoPayment origin (e.g. external_api)
failedReasonNoFailure reason (only if payment failed)

How to identify payment type by reference:

Reference infixTypeEnvironment
-PXT-Online paymentLive
-PXTT-Online paymentTest
-POSX-POS paymentLive
-POSXT-POS paymentTest

Successful POS payment example:

{
  "event_code": "AUTHORISATION",
  "data": {
    "amount": 8500,
    "currency": "EUR",
    "paymentReference": "SHOP01-POSX-17195300001234",
    "paymentStatus": "AUTHORISED",
    "description": "Venta mostrador #205",
    "customReference": "VENTA-205-20260628",
    "paymentSource": "external_api"
  }
}

Failed payment example (declined):

{
  "event_code": "AUTHORISATION",
  "data": {
    "amount": 15000,
    "currency": "EUR",
    "paymentReference": "SHOP01-PXT-17195000005678",
    "paymentStatus": "FAILED",
    "description": "Pedido #12345",
    "failedReason": "Refused",
    "paymentSource": "external_api"
  }
}

Important: Webhooks are sent for both successful and failed payments. Always check the paymentStatus field to determine the result.

---

REFUND — Refund processed

Sent when a refund completes (success or failure) at the processor.

{
  "event_code": "REFUND",
  "data": {
    "amount": 5000,
    "currency": "EUR",
    "paymentReference": "SHOP01-PXT-17195000005678",
    "refundReason": "CUSTOMER REQUEST",
    "refundReference": "SHOP01-RFX-17195234567890",
    "status": "success",
    "customReference": "ORD-2026-12345",
    "paymentSource": "external_api"
  }
}
FieldAlways presentDescription
amountYesRefunded amount in cents
currencyYesISO 4217 code
paymentReferenceYesOriginal payment reference
refundReasonYesRefund reason (CUSTOMER REQUEST, FRAUD, RETURN, DUPLICATE, OTHER)
refundReferenceYesThis refund operation reference
statusYessuccess or failed
customReferenceNoYour internal reference
paymentSourceNoPayment origin
failedReasonNoFailure reason (only if status: "failed")

Failed refund example:

{
  "event_code": "REFUND",
  "data": {
    "amount": 5000,
    "currency": "EUR",
    "paymentReference": "SHOP01-PXT-17195000005678",
    "refundReason": "CUSTOMER REQUEST",
    "refundReference": "SHOP01-RFX-17195234567890",
    "status": "failed",
    "failedReason": "Insufficient balance"
  }
}

---

PREAUTHORISATION — Capture, cancellation, or extension result

Sent when an operation on a pre-authorization completes. The action field indicates the operation type.

Capture completed

{
  "event_code": "PREAUTHORISATION",
  "data": {
    "paymentReference": "SHOP01-PXT-17195123456789",
    "action": "capture",
    "result": "success",
    "amount": 45000,
    "currency": "EUR",
    "customReference": "ORD-2026-67890",
    "paymentSource": "external_api"
  }
}

Cancellation completed

{
  "event_code": "PREAUTHORISATION",
  "data": {
    "paymentReference": "SHOP01-PXT-17195123456789",
    "action": "cancellation",
    "result": "success",
    "customReference": "ORD-2026-67890",
    "paymentSource": "external_api"
  }
}

Extension completed

{
  "event_code": "PREAUTHORISATION",
  "data": {
    "paymentReference": "SHOP01-PXT-17195123456789",
    "action": "extend",
    "result": "success",
    "expirationDate": "2026-08-15T00:00:00Z",
    "customReference": "ORD-2026-67890",
    "paymentSource": "external_api"
  }
}
FieldAlways presentDescription
paymentReferenceYesPre-authorized payment reference
actionYescapture, cancellation, or extend
resultYessuccess or failure
amountOnly captureCaptured amount in cents
currencyOnly captureISO 4217 code
expirationDateOnly successful extendNew expiration date
customReferenceNoYour internal reference
paymentSourceNoPayment origin
failedReasonNoFailure reason (only if result: "failure")

Failed capture example (amount exceeds authorized):

{
  "event_code": "PREAUTHORISATION",
  "data": {
    "paymentReference": "SHOP01-PXT-17195123456789",
    "action": "capture",
    "result": "failure",
    "failedReason": "Payment already captured, cannot capture again"
  }
}

Note on modification flow: All modifications (refund, capture, cancel, extend) are asynchronous. The immediate API response returns status received, indicating the request has been accepted for processing. The final result (success or failed) arrives later via webhook. The integrator must not consider the operation complete until receiving the confirmation webhook.

---

CHARGEBACK — Chargeback

Sent when a cardholder initiates a chargeback against a payment. Chargebacks are processed by the card issuer and may take days or weeks to resolve.

{
  "event_code": "CHARGEBACK",
  "data": {
    "paymentReference": "SHOP01-PXT-17195123456789",
    "chargebackReference": "SHOP01-CHB-17195123456789",
    "action": "chargeback",
    "result": "success",
    "amount": 15000,
    "currency": "EUR",
    "reason": "Fraudulent transaction",
    "chargebackReasonCode": "10.4",
    "chargebackSchemeCode": "VISA_10_4",
    "defensePeriodEndsAt": "2026-06-15",
    "defendable": true,
    "disputeStatus": "Undefended",
    "paymentSource": "external_api"
  }
}
FieldAlways presentDescription
paymentReferenceYesOriginal payment reference
chargebackReferenceYesUnique chargeback reference
actionYesAlways chargeback
resultYessuccess or failure
amountYesAmount in cents
currencyYesISO 4217 currency
reasonYesChargeback reason
chargebackReasonCodeNoCard scheme reason code
chargebackSchemeCodeNoScheme code (Visa, Mastercard, etc.)
defensePeriodEndsAtNoDeadline to defend the chargeback
defendableNotrue if you can dispute the chargeback
disputeStatusNoDispute status (Undefended, Defended, etc.)
failedReasonNoReason if result is failure
paymentSourceNoPayment origin (external_api, etc.)

---

NOTIFICATION_OF_CHARGEBACK — Chargeback advance notice

Advance notice that a chargeback may be incoming. Not all processors send it. Lets you prepare before the formal chargeback is processed.

{
  "event_code": "NOTIFICATION_OF_CHARGEBACK",
  "data": {
    "paymentReference": "SHOP01-PXT-17195123456789",
    "chargebackReference": "SHOP01-CHB-17195123456789",
    "action": "notification_of_chargeback",
    "result": "success",
    "amount": 15000,
    "currency": "EUR",
    "reason": "Cardholder disputes transaction",
    "chargebackReasonCode": "10.4",
    "chargebackSchemeCode": "VISA_10_4",
    "defensePeriodEndsAt": "2026-06-15",
    "defendable": true,
    "disputeStatus": "Undefended",
    "autoDefended": false,
    "arn": "74027600000012345678901",
    "paymentSource": "external_api"
  }
}

Contains the same fields as CHARGEBACK plus two optional additional fields:

FieldDescription
autoDefendedtrue if the processor automatically defended the chargeback
arnAcquirer Reference Number — acquirer transaction identifier

---

Webhooks in the Test environment

In the Test environment, webhooks do not come from the real payment processor. They are generated automatically by the Avirato Payments backend immediately after the simulated operation completes.

Differences from Live

AspectLiveTest
SenderPayment processorAvirato Payments backend
When it arrivesSeconds to minutes after the operation1-3 seconds after the HTTP response
FormatIdenticalIdentical
FieldsIdenticalIdentical
Automatic retriesYes (exponential backoff, up to ~4 days)No: simulator makes a single delivery
Manual resendYes, from dashboardYes, from dashboard (same button)
HTTP success criterionHTTP 200HTTP 200

What you should know

  • The format is exactly the same: You can develop your webhook processing logic in Test and it will work in Live without changes
  • They arrive for successful AND failed payments: In both Live and Test you will receive webhooks when a payment is declined or an operation fails
  • Independent URLs per environment: Test webhooks are sent to the URL configured for Test, and Live to the Live URL. Configure both from the dashboard webhook panel
  • References use Test suffixes: paymentReference and operationReference will contain test infixes (-PXTT-, -POSXT-, -RFXT-, etc.)
  • Simple success criterion: In both Live and Test, your endpoint only needs to respond HTTP 200 for the webhook to be marked delivered. Any other status is considered failure and triggers retries (in Live) or remains failed in the dashboard (in Test).
  • Manual resend available in Test and Live: From the dashboard webhook panel you can manually resend a specific webhook. In both Test and Live, resend uses the URL currently configured for that environment, not the URL used on the original delivery. If you resend an already delivered webhook, the dashboard will ask for explicit confirmation to avoid accidental duplicates.

Test webhook example (successful online payment)

{
  "event_code": "AUTHORISATION",
  "data": {
    "amount": 15000,
    "currency": "EUR",
    "paymentReference": "SHOP01-PXTT-17195000005678",
    "paymentStatus": "AUTHORISED",
    "description": "Pedido test #12345",
    "customReference": "TEST-ORD-12345",
    "paymentSource": "external_api"
  }
}

Test webhook example (declined POS)

{
  "event_code": "AUTHORISATION",
  "data": {
    "amount": 10077,
    "currency": "EUR",
    "paymentReference": "SHOP01-POSXT-17195300001234",
    "paymentStatus": "FAILED",
    "description": "Venta test #100",
    "failedReason": "Refused",
    "paymentSource": "external_api"
  }
}

---

Quick reference

Operationevent_codeaction (if applicable)How to identify External API
Online payment completedAUTHORISATIONpaymentReference contains -PXT- (Live) or -PXTT- (Test)
POS payment completedAUTHORISATIONpaymentReference contains -POSX- (Live) or -POSXT- (Test)
Refund processedREFUNDrefundReference contains -RFX- (Live) or -RFXT- (Test)
Capture processedPREAUTHORISATIONcaptureOperation on External API payment
Cancellation processedPREAUTHORISATIONcancellationOperation on External API payment
Extension processedPREAUTHORISATIONextendOperation on External API payment
Chargeback receivedCHARGEBACKChargeback initiated by cardholder
Chargeback notificationNOTIFICATION_OF_CHARGEBACKAdvance notice of possible chargeback

---

Best practices

Idempotency on your endpoint

Your webhook endpoint may receive the same notification more than once (due to retries or processor duplicates). Make sure processing the same webhook twice does not cause problems: use paymentReference or refundReference as a deduplication key.

Fast response

Respond to the webhook as soon as possible (ideally in under 5 seconds). If you need heavy processing, queue the work and respond immediately with HTTP 200.

Do not rely on a single channel

Use webhooks as the primary channel, but also implement:

  1. Post-redirect verification: When the customer returns to urlOk, query GET /payment/session/{id} to confirm
  2. Safety polling: A periodic process that queries old pending sessions to detect completed payments you have not processed