Plugs is Aforo's webhook receiver — a durable inbound endpoint that transforms raw third-party webhook payloads into normalized Aforo usage events. Instead of writing custom event-parsing code for every provider you integrate, you configure a Plug once in the Aforo UI and point the provider's webhook settings at Aforo.
Aforo handles the hard parts: HMAC signature verification, JSONPath field extraction, deduplication (using the provider's event ID), and delivery retries if your downstream billing pipeline is temporarily unavailable. Your engineering team touches nothing.
Every inbound webhook is verified against the provider's HMAC signature before processing
Automatic deduplication
Provider event IDs are tracked for 72 hours; replays and retries are silently dropped
Audit trail
Every received, verified, and rejected webhook is logged with the raw payload for replay
INFO
Plugs routes to Aforo's usage-ingestor at POST /v1/ingest/webhook/{sourceId}. The sourceId is a stable UUID generated when you create the webhook source — it is the only routing identifier. No tenant ID or API key is required in the URL, because the source record is tenant-scoped server-side.
Navigate to Commercial → Webhook Sources → New Source in the Aforo admin. Each source represents a single provider integration. You can create multiple sources for the same provider — for example, one Stripe source per environment (production, staging).
Source Configuration Fields
FieldDescription
ProviderSelect from the built-in template list (Stripe, Twilio, etc.) or choose "Custom" for bespoke payloads.
Display NameHuman-readable label shown in the ingestion log and audit trail.
Metric IDThe Aforo billable unit that will receive the extracted usage quantity.
Signing SecretThe HMAC secret provided by the third-party provider. Stored encrypted with AES-256-GCM.
Event FilterOptional: a comma-separated list of event type strings. Payloads that do not match are dropped before extraction.
Field MappingsJSONPath expressions that extract quantity, tenant ID, timestamp, and custom properties from the payload.
After saving, Aforo generates a webhook URL unique to the source:
Every inbound request is verified before any payload processing occurs. An unverified or tampered webhook returns 401 Unauthorized and is logged — but never processed. The raw body is never parsed until signature validation passes.
Bare hexX-Hub-Signature-256sha256=a3b4c5d6e7f8a1b2c3d4...
Raw SHA-256X-Signature-256a3b4c5d6e7f8a1b2c3d4e5f6...
SHA-1 legacyX-Hub-Signaturesha1=f1e2d3c4b5a6...
Custom header(configured per source)any format with configurable prefix
Verification uses MessageDigest.isEqual() — a constant-time byte comparison — to prevent timing attacks. A missing or empty signing secret returns 401 rather than a 500, so misconfigured sources fail closed and never leak config state to probing attackers.
WARNING
To test a new source without signature verification during development, set aforo.event-driven-sync.webhook.verify-signatures: false in your local config. Never disable signature verification in production. This flag is ignored unless the service is running with the dev Spring profile.
Once a webhook passes signature verification, Aforo applies your JSONPath field mapping to extract the five standard usage event fields from the raw payload. Each mapping is a JSONPath expression that resolves to a scalar value within the provider's JSON body.
Built-in provider templates pre-fill the field mappings and signature header configuration. Select a template when creating a source and adjust any fields that differ from the defaults.
ProviderSignature HeaderSignature FormatCommon Use Case
Custom(configurable)Any HMAC-SHA256 or SHA-1Internal services and bespoke providers
Sending a Test Webhook with curl
The example below simulates a Stripe charge.succeeded event being received by Aforo. Replace YOUR_SOURCE_ID with the ID from the source you created and compute the HMAC with your signing secret.
Aforo returns 200 OK for unknown event types (types not in your event_filter list) without logging an error. This prevents the provider from retrying forever on events you deliberately ignore. Unknown event types are silently discarded after signature verification.