Skip to main content
Webhooks are HTTP callbacks that send real-time POST requests to your configured endpoints when specific events occur in Stora. When an event happens — such as an invoice being paid or a unit becoming occupied — Stora immediately notifies all endpoints subscribed to that event type.

Getting started

1

Create a webhook endpoint

Use the Webhook Endpoints API to register a publicly accessible HTTPS URL, the event types you want to subscribe to, and the API version.
2

Store your secret key

When you create an endpoint, Stora generates a secret key. Store it securely — you’ll use it to verify incoming requests.
3

Handle incoming events

Build a handler at your URL that verifies the signature, processes the event, and returns a 2xx response.

Payload structure

Every webhook delivers a JSON payload with this structure:
{
  "event": {
    "id": "evt_1234567890",
    "type": "invoice.paid",
    "api_version": "2025-09",
    "created_at": "2025-01-15T10:30:00Z",
    "data": {
      "invoice": {
        // ... full invoice resource
      }
    }
  }
}
FieldDescription
event.idUnique identifier for the event — use this for idempotency
event.typeThe event type (e.g. invoice.paid, contact.created)
event.api_versionMatches your endpoint’s API version — determines the data structure
event.created_atISO 8601 timestamp of when the event occurred
event.dataThe resource that triggered the event, keyed by resource type

Headers

Every webhook request includes these headers:
HeaderDescription
Content-Typeapplication/json
User-AgentStora-Webhooks/1.0
X-Stora-SignatureHMAC signature for verification (see below)
X-Stora-Request-IdUnique ID for this delivery attempt — useful for debugging

Signature verification

All webhook requests are signed using HMAC SHA256. Always verify the signature before processing. The signature is in the X-Stora-Signature header:
t={timestamp},v1={signature}
To verify:
  1. Extract the timestamp (t) and signature (v1) from the header
  2. Reconstruct the signed payload: {timestamp}.{raw_request_body}
  3. Compute the HMAC SHA256 using your endpoint’s secret key
  4. Compare the computed signature with v1
  5. Optionally, check the timestamp is recent to prevent replay attacks
def verify_webhook_signature(request_body, signature_header, secret)
  parts = signature_header.split(',').map { |p| p.split('=').last }
  timestamp = parts[0]
  signature = parts[1]

  signed_payload = "#{timestamp}.#{request_body}"
  computed = OpenSSL::HMAC.hexdigest('SHA256', secret, signed_payload)

  computed == signature
end

Retries

Stora automatically retries failed deliveries up to 6 times:
AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry6 hours
6th retry12 hours
A delivery is retried when your endpoint returns a non-2xx status code, a network error occurs, or the request times out (20-second limit). After 6 failed attempts, the delivery is marked as failed. If the endpoint is deleted or disabled before a scheduled retry, pending retries are cancelled.

Best practices

Return a 2xx response as quickly as possible — even if you process the event asynchronously. This prevents unnecessary retries.
  • Implement idempotency — use event.id to ensure you don’t process the same event twice. Store processed event IDs and check before processing.
  • Process asynchronously — for time-consuming operations, queue the webhook for background processing after returning a success response.
  • Log the request ID — use X-Stora-Request-Id to correlate retry attempts when debugging.
  • Validate signatures — always verify the HMAC signature before processing.
  • Monitor your endpoint — extended downtime may exhaust all retry attempts.

Available events

EventDescription
contact.createdTriggered when a contact is created.
contact.updatedTriggered when the contact is updated.
EventDescription
contract.createdTriggered when a contract is created.
contract.signedTriggered when a contract is signed.
EventDescription
coupon.createdTriggered when a coupon is created.
coupon.updatedTriggered when the coupon is updated.
EventDescription
credit_note.createdTriggered when a new credit is created.
credit_note.updatedTriggered when a credit note is updated.
EventDescription
deal.createdTriggered when a deal is created.
deal.lostTriggered when a deal is lost.
deal.reopenedTriggered when a deal is reopened
deal.updatedTriggered when a deal is updated.
deal.wonTriggered when a deal is won.
EventDescription
identity_verification.cancelledTriggered when an identity verification is cancelled
identity_verification.failedTriggered when an identity verification failed
identity_verification.processingTriggered when an identity verification is processing
identity_verification.succeededTriggered when an identity verification succeeded
EventDescription
invoice.createdTriggered when a new invoice is created.
invoice.finalizedTriggered when an invoice is finalized.
invoice.marked_uncollectibleTriggered when an invoice is marked as uncollectible.
invoice.paidTriggered when an invoice is mark as paid.
invoice.updatedTriggered when an invoice is updated.
EventDescription
note.createdTriggered when a note is created.
note.updatedTriggered when a note is updated.
EventDescription
order.completedTriggered when the order is completed.
order.createdTriggered when a new order is created.
order.finalizedTriggered when a new order is finalized (ready to be paid).
EventDescription
protection_level.createdTriggered when a protection level is created.
protection_level.updatedTriggered when a protection level is updated.
EventDescription
subscription.cancelledTriggered when the subscription is cancelled.
subscription.createdTriggered when the subscription is created.
subscription.endedTriggered when the subscription is ended.
subscription.startedTriggered when the subscription is started.
EventDescription
task.completedTriggered when the task is completed.
task.createdTriggered when a task is created.
task.reopenedTriggered when a completed task is reopened.
task.updatedTriggered when a task is updated.
EventDescription
tenancy.createdTriggered when a tenancy is created.
tenancy.startedTriggered when the tenancy is started.
EventDescription
unit.availableTriggered when a unit is made available.
unit.createdTriggered when a unit is created.
unit.deallocatedTriggered when a unit is deallocated.
unit.occupiedTriggered when a unit is occupied.
unit.overlockedTriggered when a unit is overlocked.
unit.repossessedTriggered when a unit is repossessed.
unit.reservedTriggered when a unit is reserved.
unit.unavailableTriggered when a unit is made unavailable.
unit.updatedTriggered when a unit is updated.
EventDescription
unit_type.createdTriggered when a unit type is created.
unit_type.updatedTriggered when a unit type is updated.
For full payload schemas, see the Webhooks section in the API reference.