Skip to main content

Altruon Webhooks

Altruon Webhooks notify your server in real time when something happens on your Altruon account: a subscription is created, an invoice is paid, a payment fails, a checkout session completes, and more.

Not to be confused with provider webhooks. The webhooks described in the gateway and billing provider guides are events Altruon receives from your providers. This page covers events Altruon sends to you.

Each webhook is an HTTPS POST with a JSON payload, signed with a per-endpoint secret so you can verify it really came from Altruon.


How it works​

  1. You register an endpoint (an HTTPS URL on your server) in the backoffice and choose which events it should receive.
  2. Altruon generates a signing secret for the endpoint (shown once — store it securely).
  3. When a subscribed event occurs, Altruon POSTs a signed JSON event envelope to your URL.
  4. Your server verifies the signature, responds with a 2xx status, and processes the event asynchronously.
  5. Failed deliveries are retried automatically with increasing delays, and every attempt is visible in the backoffice delivery log.

Webhook payloads are intentionally thin: they tell you what happened and to which resource. Fetch the full, current state from the Altruon API (e.g. the Transaction Details endpoint) rather than relying on payload contents that may be stale by the time you process the event.


Part 1: Create a webhook endpoint​

  • Go to Settings > Webhooks.
  • Click Add endpoint.
Webhooks list with delivery analytics and the Add endpoint button

The creation flow has three steps:

Step 1 — Endpoint details​

FieldRequiredDescription
Endpoint URLYesYour HTTPS URL, e.g. https://api.your-site.com/webhooks/altruon. Plain http://, credentials in the URL, and private/loopback addresses are rejected.
DescriptionNoA label for your team, e.g. Production billing sync.

You can click Send test event at this step: Altruon sends an unsigned webhook.ping to the URL to confirm it is reachable and responds with a 2xx before you save.

Create webhook endpoint: URL, description and test event

Step 2 — Select events​

Pick the events this endpoint should receive (at least one). Events are grouped by resource (subscription, invoice, payment, …) and searchable. See the full event list below.

Event selection grouped by resource

Step 3 — Review and get your signing secret​

After you confirm, Altruon shows the endpoint's signing secret (format whsec_...).

The secret is displayed only once. Copy it now and store it in your secret manager. Afterwards, the backoffice only shows the last 4 characters.

Signing secret shown once after endpoint creation

Good to know: you can have up to 2 active endpoints per site. Use the second one for a staging consumer or during a migration.


Event types​

The type field of the payload contains one of the following values:

Subscription​

EventSent when
subscription.createdA subscription is created (including trial signups)
subscription.activatedA subscription becomes active (e.g. at trial end)
subscription.pausedA subscription is paused
subscription.resumedA paused subscription is resumed
subscription.cancelledA subscription is cancelled
subscription.pre_debit_notification.sentA pre-debit notification was sent to the customer (e.g. Pix Automático)

Invoice​

EventSent when
invoice.createdAn invoice is generated by the billing platform
invoice.paidAn invoice is fully paid
invoice.dunning_startedPayment collection failed and retry (dunning) started
invoice.dunning_exhaustedAll dunning retries failed
invoice.refundedAn invoice is refunded

Payment​

EventSent when
payment.processingA payment is being processed by the gateway
payment.succeededA payment succeeded
payment.failedA payment failed
payment.retry_failedA recurring payment retry failed
payment.refundedA payment is refunded

Refund​

EventSent when
refund.processingA refund is being processed
refund.succeededA refund succeeded
refund.failedA refund failed

Customer​

EventSent when
customer.createdA customer is created
customer.updatedA customer is updated

Checkout session​

EventSent when
checkout.session.completedA checkout session completed successfully
checkout.session.expiredA checkout session expired without payment

System​

EventSent when
webhook.pingTest event triggered from the backoffice

Event payload​

Every event is delivered as a JSON envelope:

{
"id": "evt_7f3c2a1b-8d4e-4f6a-9b2c-1e5d7a8f9c0b",
"type": "payment.succeeded",
"created_at": "2026-07-14T13:07:00.000Z",
"domain": "your-tenant.altruon.io",
"livemode": false,
"data": {
"object": {
"resourceType": "PAYMENT",
"source": "gateway",
"providerRef": {
"provider": "PAGBRASIL",
"resourceId": "ORD123456",
"connectionId": "550e8400-e29b-41d4-a716-446655440000"
},
"fetchHint": "Use providerRef.resourceId to query platform or Altruon"
},
"previous_attributes": null,
"truncated": false
}
}
FieldTypeDescription
idstringUnique event ID (evt_ prefix). Use it to deduplicate: retries of the same event reuse the same ID.
typestringOne of the event types above.
created_atstringISO 8601 timestamp of when the event occurred.
domainstringYour tenant domain. "test" for test events sent from the backoffice.
livemodebooleantrue for production events, false for sandbox/test.
data.objectobjectThe affected resource. resourceType is one of SUBSCRIPTION, PAYMENT, CUSTOMER, INVOICE, REFUND, ORDER.
data.previous_attributesobject | nullOn update events, the fields that changed (previous values). null otherwise.
data.truncatedbooleantrue if the payload exceeded the 256 KB limit and was truncated.

Verify signatures​

Every delivery includes an Altruon-Signature header:

Altruon-Signature: t=1752498420,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
  • t — Unix timestamp (seconds) of when the delivery was signed.
  • v1 — hex-encoded HMAC-SHA256 of the string {t}.{raw request body}, keyed with your endpoint's whsec_... secret. During a secret rotation overlap the header can contain two v1 entries; the signature is valid if any of them matches.

To verify:

  1. Parse t and all v1 values from the header.
  2. Reject the request if t is more than 5 minutes away from the current time (replay protection).
  3. Compute HMAC-SHA256(secret, "{t}." + rawBody) over the raw request body (before any JSON parsing).
  4. Compare your computed hex digest against each v1 value using a constant-time comparison.

Node.js / Express​

const crypto = require('crypto');
const express = require('express');

const app = express();
const TOLERANCE_SECONDS = 300;

function verifyAltruonSignature(rawBody, signatureHeader, secret) {
const elements = (signatureHeader || '').split(',');
const timestamp = elements.find((e) => e.startsWith('t='))?.slice(2);
const signatures = elements.filter((e) => e.startsWith('v1=')).map((e) => e.slice(3));

if (!timestamp || signatures.length === 0) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) return false;

const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');

return signatures.some((sig) => {
try {
return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(sig, 'hex'));
} catch {
return false;
}
});
}

// Important: use the RAW body for verification, not the parsed JSON
app.post('/webhooks/altruon', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body.toString('utf8');
const valid = verifyAltruonSignature(
rawBody,
req.headers['altruon-signature'],
process.env.ALTRUON_WEBHOOK_SECRET
);

if (!valid) return res.status(400).send('Invalid signature');

const event = JSON.parse(rawBody);

// Acknowledge fast, process asynchronously (queue, job, etc.)
// Deduplicate on event.id — retries reuse the same ID.
res.sendStatus(200);
});

Python​

import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300

def verify_altruon_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
elements = (signature_header or "").split(",")
timestamp = next((e[2:] for e in elements if e.startswith("t=")), None)
signatures = [e[3:] for e in elements if e.startswith("v1=")]

if not timestamp or not signatures:
return False
if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS:
return False

signed_payload = f"{timestamp}.".encode() + raw_body
expected = hmac.new(secret.encode(), signed_payload, hashlib.sha256).hexdigest()

return any(hmac.compare_digest(expected, sig) for sig in signatures)

Test pings sent from Step 1 of the create flow are unsigned (there is no secret yet). Once the endpoint is created, use Send Test on the endpoint detail page to receive a properly signed webhook.ping and validate your verification code end to end.


Delivery and retries​

MethodPOST, Content-Type: application/json
User agentAltruon-Webhook/1.0
Timeout10 seconds — respond within this window
SuccessAny 2xx response status
RetriesFailed deliveries are retried with increasing delays (roughly 2h, 12h, then 24h after each failure), up to 4 total attempts
OrderingDeliveries to a given endpoint are sent in order, but retries mean events can arrive out of order — use created_at and fetch current state from the API
IdempotencyRetries reuse the same event id — deduplicate on it

What is retried: timeouts, connection errors, HTTP 5xx, 429, and a few transient 4xx statuses (e.g. 408, 409). Most other 4xx responses (e.g. 401, 400) are treated as permanent failures and are not retried.

Automatic disabling: an endpoint is automatically disabled (and must be manually re-enabled) after 50 consecutive failures, or when its failure rate stays at 95%+ over 3 days. Keep your consumer healthy — respond 2xx quickly and do the heavy lifting asynchronously.


Manage and monitor​

The Settings > Webhooks list shows, per endpoint: status (Active/Inactive), subscribed event count, last delivery, and success rate — with aggregate analytics cards on top. Click an endpoint to open its detail page:

  • Deliveries — every attempt with event type, status, response code, duration, and the full payload sent. Failed deliveries can be resent manually.
  • Events — the endpoint's event subscriptions, editable at any time.
  • Settings — URL, description, status, and the signing secret preview (last 4 characters).

You can enable/disable an endpoint from the list at any time; disabled endpoints receive no deliveries.


Best practices​

  • Verify every request with the signature check above; reject anything invalid with a 400.
  • Respond fast — return 200 immediately and process the event on a queue/worker. Slow handlers hit the 10-second timeout and trigger retries.
  • Deduplicate on id — you will occasionally receive the same event more than once.
  • Fetch, don't trust snapshots — use the event as a trigger and read the current resource state from the Altruon API.
  • Don't rely on ordering — a payment.succeeded retry can arrive after a later invoice.paid.
  • Use livemode to keep sandbox and production events from crossing environments.