SIGNALS Documentation
API Reference

Webhooks

Subscribe to real-time event notifications delivered to your server via HTTPS.

Overview

Webhooks let you receive real-time notifications when events happen in Signals. Register an endpoint URL, choose which events to subscribe to, and Signals will send a signed HTTP POST to your server whenever those events occur.

Managing Webhooks

Webhooks can be managed via the API (/api/v1/webhooks) or from the admin panel at Admin > Settings > Webhooks.

Events

The event catalogue is owned by the shared EventRegistry. Existing wildcard (*) subscriptions expand automatically when a new webhook-visible core or plugin event is registered.

Additional Registry Events

Event Trigger
credit_note.updated A credit note is updated
dispatch.stage_advanced A dispatch advances to its next stage
invoice_payment.updated An invoice payment is updated
list_design.created A list is created
list_design.deleted A list is deleted
list_design.updated A list is updated
list_entry.created A list entry is created
list_entry.deleted A list entry is deleted
list_entry.updated A list entry is updated
company_tax_type.created An company tax type is created
company_tax_type.deleted An company tax type is deleted
company_tax_type.updated An company tax type is updated
catalogue_item.flightcase_config.updated A catalogue item flightcase configuration is updated
catalogue_item_tax_type.created A product tax type is created
catalogue_item_tax_type.deleted A product tax type is deleted
catalogue_item_tax_type.updated A product tax type is updated
returns.closed A return is closed
returns.disposition_recorded A return disposition is recorded
returns.stage_advanced A return advances to its next stage
scan_session.opened A scan session is opened
scannable_identifier.deactivated A scannable identifier is deactivated
scannable_identifier.registered A scannable identifier is registered
stock_check.discrepancy_dismissed A stock-check discrepancy is dismissed
stock_check.sign_off_cleared A stock-check sign-off is cleared
stock_check.signed_off A stock check is signed off

Accounts

Event Trigger
account.created A new account is created
account.updated A account's details are updated
account.archived A account is archived
account.restored An archived account is restored
account.deleted A account is permanently deleted
account.merged Two accounts are merged (secondary archived into primary)
account.anonymised A account's personally identifiable information is erased
account.vat_validated A account's VAT number is validated (VIES or manual confirmation)

Catalogue items

Event Trigger
catalogue_item.created A new catalogue item is created
catalogue_item.updated A catalogue item's details are updated
catalogue_item.archived A catalogue item is archived (soft-deleted, recoverable)
catalogue_item.restored An archived catalogue item is restored
catalogue_item.deleted A catalogue item is deleted
catalogue_item.merged Two catalogue items are merged
catalogue_item_group.created A new catalogue item group is created
catalogue_item_group.updated A catalogue item group is updated
catalogue_item_group.deleted A catalogue item group is deleted
accessory.created An accessory link is added to a catalogue item
accessory.updated An accessory link is updated
accessory.deleted An accessory link is removed
kit_component.created A kit bill-of-materials component is added
kit_component.updated A kit bill-of-materials component is updated
kit_component.deleted A kit bill-of-materials component is removed

Stock

Event Trigger
asset.created An asset record is created
asset.updated An asset is updated
asset.deleted An asset is deleted
asset_transaction.created An asset transaction is recorded
asset_transaction.deleted An asset transaction is deleted

Flightcases

Event Trigger
flightcase.created A flightcase is created (catalogue-item-backed or temporary)
flightcase.updated Flightcase metadata or scan mode is updated
flightcase.item.packed An item is packed into a flightcase
flightcase.item.unpacked An item is unpacked from a flightcase
flightcase.item.transferred An item is moved between flightcases in one step
flightcase.nested A flightcase is packed inside another flightcase
flightcase.sealed A flightcase passes the seal gate and is sealed
flightcase.unsealed A sealed flightcase is reopened (manual or transfer-triggered)
flightcase.dissolved A flightcase is manually or dispatch-dissolved
flightcase.dispatched A kit/hybrid flightcase is dispatched as a persistent unit
flightcase.returned A kit/hybrid flightcase is returned from a rental
flightcase.repack.started Repack-on-return initiated for a transport flightcase housing
flightcase.checkin_verified A flightcase's contents are verified during check-in

Activities

Event Trigger
activity.created A new activity is created
activity.updated An activity is updated
activity.deleted An activity is deleted
activity.completed An activity is marked as completed

Addresses

Event Trigger
address.created An address is created
address.updated An address is updated
address.deleted An address is deleted

Conversations

Event Trigger
conversation.created A conversation note/email/system entry is created
conversation.updated A conversation is updated
conversation.deleted A conversation is deleted
conversation.mentioned A user-type account is @mentioned in a conversation or comment
conversation_comment.created A comment is added to a conversation
conversation_comment.updated A conversation comment is edited
conversation_comment.deleted A conversation comment is deleted

Rate specifications

Event Trigger
rate_specification.created A rate specification is created
rate_specification.updated A rate specification is updated
rate_specification.deleted A rate specification is deleted
catalogue_item_rate.created A catalogue item rate is assigned
catalogue_item_rate.updated A catalogue item rate is updated
catalogue_item_rate.deleted A catalogue item rate is removed

Tax

Event Trigger
tax_rate.created A tax rate is created
tax_rate.updated A tax rate is updated
tax_rate.deleted A tax rate is deleted
tax_rule.created A tax rule is created
tax_rule.updated A tax rule is updated
tax_rule.deleted A tax rule is deleted
tax_zone.created A tax zone is created
tax_zone.updated A tax zone is updated
tax_zone.deleted A tax zone is deleted
tax_category.created A tax category is created
tax_category.updated A tax category is updated
tax_category.deleted A tax category is deleted

Pricing

Event Trigger
discount_category.created A discount category is created
discount_category.updated A discount category is updated
discount_category.deleted A discount category is deleted
discount_rule.created A discount rule is created
discount_rule.updated A discount rule is updated
discount_rule.deleted A discount rule is deleted
surcharge.created A surcharge is created
surcharge.updated A surcharge is updated
surcharge.deleted A surcharge is deleted
surcharge_group.created A surcharge group is created
surcharge_group.updated A surcharge group is updated
surcharge_group.deleted A surcharge group is deleted
price_category.created A price category is created
price_category.updated A price category is updated
price_category.deleted A price category is deleted
bundle_rule.created A bundle rule is created
bundle_rule.updated A bundle rule is updated
bundle_rule.deleted A bundle rule is deleted

Warehouses

Event Trigger
warehouse.created A warehouse location is created
warehouse.updated A warehouse location is updated
warehouse.deleted A warehouse location is deleted

Currencies

Event Trigger
currency.created A currency is created
currency.updated A currency is updated
currency.deleted A currency is deleted

Exchange Rates

Event Trigger
exchange_rate.created An exchange rate is created
exchange_rate.updated An exchange rate is updated
exchange_rate.deleted An exchange rate is deleted

Users

Event Trigger
user.created A new user is invited or created
user.updated A user's profile or roles are updated
user.deactivated A user account is deactivated
user.deleted A user is deleted

Roles

Event Trigger
role.created A new role is created
role.updated A role's name or permissions are modified
role.deleted A role is deleted

Settings

Event Trigger
settings.updated Application settings are changed

Rentals

Rental events fire on every committed mutation of a rental, its line items, costs, allocated assets, and quote versions. They are never re-delivered when the event warehouse is rebuilt.

Event Trigger
rental.created A new rental (quote) is created
rental.updated A rental's header fields are updated
rental.quoted A rental is marked as quoted
rental.converted_to_order A quote is converted to an order
rental.reverted_to_quotation An order is reverted back to a quotation
rental.reverted_to_enquiry A quotation is reverted back to a draft
rental.reinstated A lost, dead, postponed, or cancelled rental is reinstated
rental.reopened A completed order is re-opened back to an active order
rental.locks_applied FX/tax locks are applied to an order
rental.locks_released An order's FX/tax locks are released for re-pricing
rental.status_changed A rental's status changes
rental.status_promoted A rental's status is promoted along its workflow
rental.cloned A rental is cloned into a new rental
rental.deleted A rental is soft-deleted
rental.restored A soft-deleted rental is restored
rental.deal_price_set A negotiated deal total is set
rental.deal_price_cleared A negotiated deal total is cleared
rental.group_deal_price_set A negotiated deal total is set on a line-item group
rental.group_deal_price_cleared A negotiated deal total is cleared from a line-item group
rental.pricing_recalculated Rental pricing is recalculated (rates, tax, surcharges)
rental.late_return An order still has equipment out past its scheduled end date
rental.quote_expiring A quote is approaching its quote_invalid_at expiry
rental.discount_reset Applied discounts are cleared from a rental
rental.discount_category_applied A discount category is applied to a rental
rental.discount_manually_overridden A discount category percentage is manually overridden
rental.item_added A line item is added
rental.item_removed A line item is removed
rental.item_quantity_changed A line item's quantity changes
rental.item_dates_changed A line item's hire dates change
rental.item_discount_set A line item discount is set
rental.item_deal_price_discount_set A line item deal-price-derived discount is set or cleared
rental.item_optional_toggled A line item is toggled optional/required
rental.item_price_overridden A line item unit price is overridden
rental.item_substituted A line item catalogue item is substituted
rental.item_surcharge_overridden A line item surcharge amount is manually overridden
rental.items_restructured A rental's line-item tree is restructured
rental.items_reordered A rental's line items are reordered
rental.surcharge_group_toggled A surcharge group is enabled or disabled on a rental
rental.cost_added An additional cost is added
rental.cost_updated An additional cost is updated
rental.cost_removed An additional cost is removed
rental.version_created A new quote version is created
rental.version_activated A quote version is made active
rental.version_accepted A quote version is accepted by the customer
rental.version_declined A quote version is declined
rental.version_sent A quote version is sent to the customer
rental.version_relabelled A quote version's label is changed
rental.version_superseded A quote version is superseded by a newer one
rental.version_deleted A quote version is deleted
rental.asset_allocated A barcoded asset is allocated to a line item
rental.asset_deallocated A barcoded asset is deallocated
rental.asset_substituted An allocated asset is substituted for another
rental.asset_prepared An asset is marked prepared for dispatch
rental.asset_preparation_reverted An asset's prepared state is reverted
rental.asset_checked An asset is scan-checked
rental.asset_dispatched An asset is dispatched
rental.asset_on_hire An asset is marked on-hire
rental.asset_returned An asset is returned
rental.asset_status_reverted An asset's lifecycle status is reverted
rental.asset_flightcase_set An asset is placed in a flightcase
rental.asset_flightcase_cleared An asset is removed from its flightcase
rental.bulk_dispatched A bulk (non-barcoded) quantity is dispatched
rental.bulk_returned A loose quantity is returned
rental.bulk_quantity_adjusted A bulk dispatched/returned quantity is adjusted

Communication Drafts

Payloads are lean envelopes — id, status, and the title pointer. A draft's body is customer prose that has not (yet) cleared the approval gate, so it is never broadcast; re-read /api/v1/communication_drafts/{id} for the content.

Event Trigger
communication_draft.created A customer communication is drafted for approval
communication_draft.approved A human approves a draft (optionally editing it first)
communication_draft.discarded A draft is rejected and can never be sent
communication_draft.sent An approved draft is handed to the delivery pipeline

Documents

Event Trigger
document.created A document is generated from a template
document.pdf_generated A PDF is generated and stored for a document
document.finalised A document is finalised (locked for sharing)
document.voided A finalised document is voided
document.emailed A document is emailed to a recipient (Notification Engine, P38)
document.public_link_created A public sharing link is created for a document
document.public_link_accessed A public sharing link is accessed for the first time
document.public_link_revoked A public sharing link is revoked
document.public_link_configured A public link's expiry or PIN settings are changed
document.approval_requested A document is shared with the customer asking for approval
document.portal_question A customer asks a question from the public document portal
document.approved A customer approves a document from the public portal
document.declined A customer declines a document from the public portal
document_template.created A document template is created or duplicated
document_template.updated A document template is saved
document_template.deleted A custom document template is deleted
document_template.restored A system template is restored to its shipped default
document_template.set_as_default A template is marked as the default for its type
document_sequence.reset A document numbering sequence rolls over to a new period
document_sequence.updated A document numbering sequence is updated

Communication Templates

Event Trigger
communication_template.created A communication template is created
communication_template.updated A communication template is updated
communication_template.deleted A communication template is deleted

Email Layouts & Templates

Event Trigger
email_layout.created An email layout is created
email_layout.updated An email layout is saved or restored from a version
email_layout.deleted A custom email layout is deleted
email_layout.set_as_default An email layout is promoted to the default
email_template.created A custom email template is created in the admin UI
email_template.updated An email template is saved or restored from a version
email_template.deleted A custom email template is deleted in the admin UI

Attachments

Event Trigger
attachment.created A file is attached to an entity
attachment.deleted An attachment is deleted from an entity

Invoices

Event Trigger
invoice.created A draft invoice is generated
invoice.issued A draft invoice is issued to the customer
invoice.voided An invoice is voided
invoice.overdue An issued invoice passes its due date with an outstanding balance
payment.recorded A payment is recorded against an invoice — payload embeds payment fields (see Invoices API)
invoice.portal_payment_received A customer pays an invoice from the public portal
payment.refunded A refund is recorded against an invoice — payload embeds payment/refund fields (see Invoices API)
credit_note.issued A credit note is issued against an invoice
credit_note.draft_created A draft credit note is created (no financial effect)
credit_note.applied A draft credit note is applied and balances committed
credit_note.voided A credit note is voided

Billing Runs

Event Trigger
billing_run.created A billing run is created
billing_run.completed A billing-run issue batch finishes successfully

Availability

Event Trigger
availability.changed A catalogue-item/warehouse's availability read model has been recalculated

Shortages

Event Trigger
shortage.detected A shortage is detected for an order line
shortage.cleared A previously-detected shortage no longer exists
shortage.acknowledged A shortage is acknowledged at a confirmation gate
shortage.resolution.created A shortage resolution is created
shortage.resolution.confirmed A shortage resolution is confirmed
shortage.resolution.in_progress A shortage resolution's fulfilment has begun
shortage.resolution.fulfilled A shortage resolution completes and stock is available
shortage.resolution.partially_fulfilled A shortage resolution is partially fulfilled
shortage.resolution.failed A shortage resolution attempt fails
shortage.resolution.cancelled A shortage resolution is cancelled
shortage.waitlist.created A waitlist monitor is placed on a shortage
shortage.waitlist.matched Monitored stock becomes available
shortage.waitlist.expired A waitlist monitor expires without matching
shortage.escalated An unresolved shortage passes the configured escalation threshold

Purchase Orders

Event Trigger
purchase_order.created A draft purchase order is created
purchase_order.updated A draft purchase order is updated
purchase_order.sent A purchase order is sent to the supplier
purchase_order.received Goods are received against a purchase order
purchase_order.cancelled A purchase order is cancelled
purchase_order.deleted A draft purchase order is deleted
purchase_order_document.created A document (e.g. supplier PDF) is generated for a purchase order

Virtual Stock

Event Trigger
virtual_stock_intake.created A draft virtual stock intake is created
virtual_stock_intake.updated A draft virtual stock intake is updated
virtual_stock_intake.confirmed A virtual stock intake is confirmed
virtual_stock_intake.received Virtual stock is received into availability
virtual_stock_intake.returned Virtual stock is returned after the hire window
virtual_stock_intake.cancelled A virtual stock intake is cancelled
virtual_stock_intake.deleted A draft virtual stock intake is deleted

Vehicles

Event Trigger
vehicle.created A vehicle is created
vehicle.updated A vehicle is updated
vehicle.deleted A vehicle is deleted
vehicle.assigned A vehicle is assigned to a rental or job
vehicle.assignment_updated A vehicle assignment's details are updated
vehicle.unassigned A vehicle is unassigned

Facilities

Event Trigger
facility.created A bookable facility is created
facility.updated A bookable facility is updated
facility.deleted A bookable facility is deleted

Services

Event Trigger
service.created A labour catalogue service is created
service.updated A labour catalogue service is updated
service.deleted A labour catalogue service is deleted

Resource assignments

Event Trigger
resource_assignment.created A scheduling resource assignment is created
resource_assignment.updated A scheduling resource assignment is updated
resource_assignment.cancelled A scheduling resource assignment is cancelled
resource_assignment.confirmed A scheduling resource assignment is confirmed
resource_assignment.started A scheduling resource assignment is started
resource_assignment.completed A scheduling resource assignment is completed
resource_assignment.rescheduled A scheduling resource assignment window is changed
resource_assignment.demand_unmet Cancelling an assignment leaves service demand unmet (rental_item_id + shortfall)

Assignment route stops

Event Trigger
assignment_route_stop.created A route stop is appended to a resource assignment
assignment_route_stop.updated A route stop's location, type, times, or notes are updated
assignment_route_stop.reordered Route stops on an assignment are reordered
assignment_route_stop.deleted A route stop is removed from a resource assignment

Scheduling conflicts

Event Trigger
scheduling_conflict.detected A scheduling conflict is detected on assignment write
scheduling_conflict.resolved A scheduling conflict is resolved

Repair

Event Trigger
repair.created A stock item is placed into repair
repair.updated A repair record's details are updated
repair.released A repaired item is released back into service

Equipment tests

Event Trigger
equipment_test.created An equipment_test record is created
equipment_test.updated An equipment_test record is updated
equipment_test.deleted An equipment_test record is deleted
equipment_test.completed An equipment_test is completed
equipment_test.failed An equipment_test is recorded as failed
equipment_test_result.created A result line is recorded against an equipment_test
equipment_test_result.deleted An equipment_test result line is deleted

Asset Lifecycle

Event Trigger
asset_cost.created A maintenance/asset cost is recorded
asset_cost.updated An asset cost is updated
asset_cost.deleted An asset cost is deleted
asset_retirement.created An asset is retired (disposed)
asset_retirement.voided A stock-check-sourced retirement is voided directly (not emitted on reinstate)
asset_retirement.reinstated A retired asset is reinstated (returned to active stock)
usage_reading.recorded A usage reading (e.g. odometer/hours) is recorded against a stock unit
usage_reading.deleted A usage reading is deleted

Stock Checks

Event Trigger
stock_check.created A stock check is created
stock_check.updated A stock check is updated
stock_check.submitted A stock check is submitted for review
stock_check.completed A stock check is completed and reconciled
stock_check.reverted A completed stock check is reverted
stock_check.cancelled A stock check is cancelled
stock_check.discrepancy_resolved A stock check discrepancy is resolved (ledger/relocation applied)

Warehouse Transfers

Event Trigger
warehouse_transfer.created A warehouse transfer is created
warehouse_transfer.updated A warehouse transfer's details are updated
warehouse_transfer.dispatched A warehouse transfer is dispatched from the origin warehouse
warehouse_transfer.received A warehouse transfer is received at the destination warehouse
warehouse_transfer.cancelled A warehouse transfer is cancelled

Rental Projects

Event Trigger
rental_project.created A rental project is created
rental_project.updated A rental project's details or status are updated
rental_project.deleted A rental project is deleted (its rentals are detached)
rental.assigned_to_project A rental is assigned to a rental project
rental.detached_from_project A rental is detached from its rental project

Supplier Costs

Event Trigger
supplier_cost.created A supplier cost is recorded against a catalogue item
supplier_cost.updated A supplier cost is updated
supplier_cost.deleted A supplier cost is deleted

Dispatch & Returns

Event Trigger
dispatch.stage_advanced A rental dispatch is moved to a new dispatch stage
returns.stage_advanced A rental return is moved to a new return stage
returns.disposition_recorded A return disposition (damage, loss, write-off, …) is recorded
returns.closed A rental's returns are closed

Scannable Identifiers

Event Trigger
scannable_identifier.registered A barcode/QR/RFID identifier is registered against a scannable entity
scannable_identifier.deactivated A scannable identifier is deactivated

Scan Sessions

Event Trigger
scan_session.completed A scanning session is completed
scan_session.abandoned A scanning session is abandoned
scan_session.opened A scanning session is opened

Reporting

Event Trigger
report.generated A scheduled report finishes running and has been delivered to its subscribers

Import / Export

Event Trigger
export.completed An export job finishes successfully
export.failed An export job fails
export.deleted An export job and its generated file are deleted
import.batch.parsing An import batch begins parsing the uploaded file
import.batch.validated An import batch finishes validation
import.batch.committed An import batch finishes committing rows
import.batch.failed An import batch commit fails or pauses with errors
import.plan.started A migration / import plan begins sequential execution
import.plan.step_completed A plan step finishes committing its batch
import.plan.completed All plan steps complete successfully
import.plan.failed Plan execution pauses at a failing step (pause-not-rollback)

Updates

Event Trigger
update.started A self-hosted upgrade run begins
update.completed A self-hosted upgrade run finishes successfully
update.failed A self-hosted upgrade run stops at a failing step

Event stability & deprecation

Event names are a public contract. A subscription stores the names it wants verbatim, so a renamed or removed name simply stops matching and deliveries end silently — there is no error for the integration to see.

The policy is therefore:

  • Additive only. New events may be added at any time. A released event name is never renamed and never removed.
  • Payload changes are additive. Fields may be added to an event payload; existing fields are not renamed, retyped, or dropped. Subscribers should ignore unknown fields.
  • Deprecation, not deletion. An event that is no longer recommended is documented as deprecated (and, in future, flagged as such in the catalogue with a sunset window). It keeps firing until the sunset passes.

This is enforced in CI, not just by convention: ReleasedEventNames::NAMES (tests/Support/Events/ReleasedEventNames.php) is a frozen snapshot of every released event name, and the ratchet in tests/Feature/Architecture/EventCatalogueRatchetTest.php fails if any of them disappears from config/events.php. Adding an event is free — append the new name to the snapshot.

Payload Format

Every webhook delivery sends a JSON POST request with the event name and associated data:

{
    "event": "user.created",
    "timestamp": "2026-01-15T14:30:00Z",
    "data": {
        "user": {
            "id": 42,
            "name": "Jane Smith",
            "email": "[email protected]"
        }
    }
}

Actor attribution

Every data object carries a reserved actor_type key naming who caused the event:

Value Meaning
user A signed-in person acting in the web UI, including via the built-in API Explorer
api An external caller using an API token
agent An AI agent acting over MCP
system Scheduled work, queue jobs, artisan commands, or an internal process

It is stamped once at the dispatch boundary, so it is present regardless of which part of the application produced the event.

actor_type answers who acted, which is not the same as how the call arrived. A request replayed through the in-process kernel is classified by its caller, not by the token it carries: MCP is an agent, the API Explorer is the user driving it, and the CLI is the system — so a token-authenticated artisan run is never reported as api.

Cross-entity reference (ref)

Alongside actor_type, the data object carries an optional ref key naming the title of the event as a single addressable string:

"ref": "rental:123"

The format is entity:id — the entity slug is the first segment of the event name, and the id is the title's integer primary key. Primary keys elsewhere in the payload are unchanged; ref is an additive convenience for consumers correlating events across entities.

It is stamped at the dispatch boundary and is present when the title's id is unambiguous:

  • the payload nests the entity's own object (data.rental.id for any rental.* event), or
  • the event name is a two-segment entity.action and the payload carries a top-level id (the lean_envelope shape).

ref is absent when the title cannot be identified without guessing — for example sub-entity events such as account.address.created, whose top-level id belongs to the address rather than the account, and ad_hoc payloads that carry no id for the event's entity. Treat ref as optional and fall back to the ids in the payload body.

A producer that sets ref explicitly keeps its own value.

Payload Schemas

Most events embed the affected resource under a singular key inside data, using the same shape the REST API returns. Lifecycle events that only need to identify a record (deletions, archive/restore) send just an id.

Each event declares its payload shape in the event catalogue:

Shape data contains
full_dto the affected resource under a singular key, in the same shape the REST API returns
lean_envelope id and action only — re-read the resource endpoint for detail
ad_hoc a bespoke payload documented per event (change snapshots, progress counters)

Tax Rate Events

tax_rate.created and tax_rate.updated include the full tax rate object:

{
    "event": "tax_rate.created",
    "timestamp": "2026-01-15T14:30:00Z",
    "data": {
        "tax_rate": {
            "id": 1,
            "name": "UK Standard",
            "description": "Standard rate VAT",
            "rate": "20.0000",
            "is_active": true,
            "created_at": "2026-01-15T14:30:00Z",
            "updated_at": "2026-01-15T14:30:00Z"
        }
    }
}
Field Type Description
id integer Tax rate identifier
name string Display name (e.g. UK Standard)
description string|null Optional description
rate string Percentage as a decimal string (e.g. 20.0000)
is_active boolean Whether the rate is active
created_at / updated_at string ISO 8601 UTC timestamps

tax_rate.deleted sends only the identifier:

{
    "event": "tax_rate.deleted",
    "timestamp": "2026-01-15T14:30:00Z",
    "data": { "id": 1 }
}

Tax Rule Events

tax_rule.created and tax_rule.updated include the full tax rule object:

{
    "event": "tax_rule.created",
    "timestamp": "2026-01-15T14:30:00Z",
    "data": {
        "tax_rule": {
            "id": 5,
            "company_tax_type_id": 1,
            "catalogue_item_tax_type_id": 2,
            "tax_rate_id": 1,
            "priority": 10,
            "is_active": true,
            "created_at": "2026-01-15T14:30:00Z",
            "updated_at": "2026-01-15T14:30:00Z"
        }
    }
}
Field Type Description
id integer Tax rule identifier
company_tax_type_id integer Company tax type matched by the rule
catalogue_item_tax_type_id integer Catalogue item tax type matched by the rule
tax_rate_id integer Tax rate applied when the rule matches
priority integer Higher priority rules win when several match
is_active boolean Whether the rule is active
created_at / updated_at string ISO 8601 UTC timestamps

tax_rule.deleted sends only the identifier:

{
    "event": "tax_rule.deleted",
    "timestamp": "2026-01-15T14:30:00Z",
    "data": { "id": 5 }
}

CatalogueItem Archive & Restore Events

catalogue_item.archived and catalogue_item.restored are lifecycle transitions on an existing catalogue item and carry only its identifier. Fetch the current state from GET /api/v1/catalogue_items/{id} if you need the full record.

{
    "event": "catalogue_item.archived",
    "timestamp": "2026-01-15T14:30:00Z",
    "data": { "id": 42 }
}
Field Type Description
id integer Catalogue item identifier

Verifying Signatures

Every delivery includes an X-Signals-Signature header containing an HMAC-SHA256 signature of the request body. Verify it against the secret you received when creating the webhook:

$signature = hash_hmac('sha256', $requestBody, $webhookSecret);

if (! hash_equals($signature, $request->header('X-Signals-Signature'))) {
    abort(401, 'Invalid signature');
}

Retry Behaviour

Failed deliveries (non-2xx response or connection timeout) are retried with exponential backoff up to 6 times over approximately 12 hours. After 3 consecutive days of failures, the webhook is automatically disabled.

Re-enable a disabled webhook from the admin panel or by updating it via the API with is_active: true.

Delivery Logs

Every delivery attempt is logged. View logs via the API at GET /api/v1/webhooks/{id}/logs or from the API Console Webhooks page by clicking the logs icon on a webhook row. Deliveries also appear in the unified Request Log on the webhook_sent channel.

Outbound metering weights

Each outbound event carries a price weight (0–4) declared in WebhookEventRegistry. Weights default to 1 unless overridden at registration time. On the first delivery attempt of each job run, DeliverWebhook records the weight on the webhook_logs row and increments the webhook_sent daily rollup by the event's weight.

Weight 0 events are still logged but contribute zero weighted units toward usage totals.

Inbound webhook receiver

Signals can accept webhook POSTs from external systems via registered inbound sources. Manage sources from the API Console at /api/webhooks (Inbound tab) or programmatically when that surface is exposed.

Receive URL

POST {your-app-url}/webhooks/receive/{slug}

Replace {slug} with the source's URL slug. The route is public (no Sanctum token) — authentication is HMAC signature verification only.

Signature verification

Each source stores a secret, signature algorithm (default sha256), and header name (default X-Signature). The middleware computes:

$expected = hash_hmac($source->signature_algo, $requestBody, $source->secret);
$provided = $request->header($source->signature_header);

$valid = is_string($provided) && hash_equals($expected, $provided);

Send the HMAC digest as a plain string in the configured header (not prefixed with sha256=).

Responses

Status Condition Body
200 Valid signature {"status":"received"} — fires InboundWebhookReceived
401 Missing or invalid signature {"message":"Invalid signature"} — logged with weight 0
404 Unknown slug or inactive source Laravel 404 page

Inbound metering weights

Unlike outbound events (code-defined in WebhookEventRegistry), inbound weights are stored per source in inbound_webhook_sources.price_weight (0–4, editable in the console). Valid requests meter on the webhook_received channel; invalid signatures log detail rows but do not increment weighted units.

Inbound logs

Receipts are stored in inbound_webhook_logs and surfaced in the Request Log (webhook_received channel) and the source's logs modal in the API Console.