Inbound webhooks let external systems send JSON payloads into AlgaPSA. Each webhook has a tenant-scoped receiver URL, its own authentication settings, idempotency rules, rate limit, and a handler that either runs a registered direct action or starts a workflow.
Use this page for events sent into AlgaPSA. For events that AlgaPSA sends to your application, see outbound webhooks.
After you create an inbound webhook, configure the source system to send JSON POST requests to the public receiver URL:
https://algapsa.com/api/inbound/{tenantSlug}/{webhookSlug}tenantSlug identifies the tenant route, and webhookSlug is the URL-safe slug on the inbound webhook configuration. The receiver accepts source-specific JSON; the payload shape is defined by the sender and the field mappings you configure.
Inbound webhook configuration uses the normal API key authentication model. The generated endpoint reference includes the full request and response schemas.
Start with the generated reference for POST /api/v1/inbound-webhooks and the receiver endpoint POST /api/inbound/{tenantSlug}/{webhookSlug}.
Authentication is configured per webhook, so each source system can use the strongest method it supports. Secret values are provided at create or update time and are not returned later.
Rotate stored secret material with POST /api/v1/inbound-webhooks/{id}/rotate-secret. The replacement is returned once, so store it immediately.
Configure an optional idempotency source to make duplicate deliveries safe. The source can be a request header, such as X-Idempotency-Key, or a JSONata expression evaluated against the request body. AlgaPSA records each delivery and treats repeated keys inside the configured window as duplicate no-ops.
{
"idempotency_source": {
"type": "jsonata",
"value": "event.id"
},
"idempotency_window_seconds": 86400
}A verified delivery can run a direct action or start a workflow. Direct actions map JSON fields from the sender payload into a registered AlgaPSA action. Workflow handlers receive a verified envelope with the request body, filtered headers, delivery id, idempotency key, and received timestamp.
{
"name": "RMM device updates",
"slug": "rmm-device-updates",
"auth_type": "hmac_sha256",
"auth_config": {
"signature_header": "X-RMM-Signature",
"secret": "replace-with-shared-secret"
},
"idempotency_source": {
"type": "jsonata",
"value": "event.id"
},
"handler_type": "direct_action",
"handler_config": {
"action": "upsertAssetByExternalId",
"field_mapping": {
"external_id": "device.id",
"client_external_id": "organization.id",
"asset_type": "'workstation'",
"name": "device.hostname",
"status": "device.status",
"rmm_snapshot": "$"
}
},
"rate_limit_per_minute": 60,
"is_active": true
}Use GET /api/v1/inbound-webhooks/actions to discover the exact action names and target fields available in your tenant before creating direct action mappings.
Use POST /api/v1/inbound-webhooks/{id}/test to send a synthetic payload through the current configuration. To build mappings from a real sender payload, enable sample capture with POST /api/v1/inbound-webhooks/{id}/capture-sample; the next verified request stores its body as the webhook sample until you clear it with the matching DELETE endpoint.
Every inbound request is recorded with authentication status, dispatch status, request metadata, handler outcome, response status, duration, and replay metadata. Inspect deliveries with GET /api/v1/inbound-webhooks/{id}/deliveries and replay a stored delivery with POST /api/v1/inbound-webhooks/{id}/deliveries/{deliveryId}/replay.
POST /api/webhooks/alternative-payments is a fixed receiver for external payment events in Enterprise Edition. It uses a server-wide shared secret and has no configurable webhook slug. Community Edition returns 403.
Set ALTERNATIVE_PAYMENTS_WEBHOOK_SECRET on the server. Sign the raw request body with HMAC-SHA256 and send the hex digest, optionally prefixed with sha256=, in one of these headers, checked in order:
X-Alternative-Payments-Signature
X-Ap-Signature
X-Webhook-SignatureA missing server secret or missing or invalid signature returns 401 with {"error":"Invalid signature"}. The tenant is taken from X-Tenant-Id first, then from the payload. A minimal payment payload uses these fields:
{
"tenant_id": "your-tenant-id",
"event_id": "processor-event-id",
"invoice_id": "your-invoice-id",
"status": "paid",
"amount_cents": 12500,
"currency": "USD"
}Both a tenant and event identifier are required; missing either returns 400 with {"error":"Missing tenant_id or event_id"}. Successful payment statuses are paid, completed, succeeded, and success. These require an invoice identifier and a positive amount in cents. Other statuses are acknowledged with processed: false and reason: "non_success_status".
Where the payment_webhook_events table is present, events are deduplicated by tenant, provider, and external event identifier. Duplicate deliveries return received: true, processed: true, and duplicate: true without processing again. Inspect the response body as well as the HTTP status: a payment-recording failure can return 200 with processed: false and an error.
GET /api/webhooks/alternative-payments returns health metadata including status: "ok", the provider name, timestamp, and whether the signing secret is configured. It does not verify that a payment can be recorded.