Skip to main content
Listen to real-time events in your gigstack account. The Webhooks API allows you to configure endpoints that receive event notifications when specific actions occur in your system.

Overview

Webhooks enable you to build event-driven integrations by receiving automatic HTTP POST notifications when events occur in your gigstack account. Configure which events to listen for and where to receive them.

Key Features

  • Real-time Notifications - Instant event delivery to your endpoints
  • Event Filtering - Subscribe only to events you need
  • Status Control - Enable/disable webhooks without deletion
  • Multi-event Support - Single webhook can listen to multiple event types
  • Automatic Retries - Failed deliveries of resource events are retried with backoff (see Delivery Behavior)

Endpoints

List Webhooks

Retrieve all configured webhooks for your team. Query Parameters:
  • limit (integer, 1-100) - Number of results to return (default: 10)
  • status (string) - Filter by status: active or inactive
  • team (string) - gigstack Connect: Target team ID
Example Request:
Example Response:

Get Webhook

Retrieve details of a specific webhook. Example Request:
Example Response:

Create Webhook

Create a new webhook endpoint to receive event notifications. Request Body:
Required Fields:
  • url (string) - Endpoint URL that receives the events. Any valid URL is accepted; use HTTPS in production
  • events (array) - List of event types to subscribe to (see Available Events below)
Optional Fields:
  • description (string) - Human-readable description of the webhook
  • status (string) - active or inactive (default: active)
Example Request:
Example Response:
Store secret now. It is returned only in this response — GET, PUT and list calls never include it, and it cannot be revealed or rotated later. It signs sat.invoice.synced and invoice_batch.completed deliveries only (see Verifying Signatures); resource events such as payment.succeeded are not signed with it. If you lose it, delete the webhook and create a new one.

Update Webhook

Update an existing webhook’s configuration. Request Body: All fields are optional. Only include fields you want to update.
Example Request:

Delete Webhook

Permanently delete a webhook endpoint. Example Request:
Example Response:

Available Events

A webhook can subscribe to any of these event types:
Delivery families.
  • Resource events (payment.*, invoice.*, receipt.*, customer.*, service.*) use the body for the webhook’s payload version. They are retried and are not signed.
  • sat.invoice.synced always uses its own body. It is signed with the webhook’s secret and is sent only once.
  • invoice_batch.completed always uses its own body, and is delivered like sat.invoice.synced: signed and sent only once. Despite the prefix, it is not an invoice.* resource event.

Webhook Structure

Webhook Object

Webhook Payload

Each delivery is an HTTP POST to your URL with Content-Type: application/json. Respond with any 2xx status within 10 seconds.

Payload versions

Each webhook sends resource events in one of two body formats: The webhook object returned by the API does not include its version, but you can tell the two apart by shape:
  • A v2 body has type and data.object.
  • A v1 body has event, team and webhook.
Saving a webhook in the dashboard switches it to v2.

v1 body

A v1 body has no unique delivery id. Do not permanently de-duplicate by event plus data.id: two legitimate customer.updated events for one customer have the same pair. Store each received notification and make processing idempotent by reading the current resource and upserting its state in your application. Include the team and mode in your local resource key. For irreversible effects, reconcile the business operation rather than treating a notification as authority to repeat it. Prefer v2 when you need a stable delivery ID. Saving a webhook in the dashboard switches it to v2; update your parser before doing so. A duplicate v2 delivery can contain a newer resource state, so use it as a signal to reconcile current state, not as a permanent historical snapshot. Teams on the legacy v1.1 default receive the same object wrapped as { "payload": { … } }. For invoice.created, their data also includes a customer object with the full address, and date is shifted by −6 hours.

v2 body

Headers

Resource events are not signed. To authenticate them:
  1. Add a secret header to the webhook in the dashboard, for example Authorization: Bearer <random token>.
  2. Reject any request that doesn’t carry it.
Custom headers can’t be set through the API. Webhooks created before signing was introduced have no secret, and their sat.invoice.synced and invoice_batch.completed deliveries have no X-Gigstack-Signature header. Recreate them to get one.

SAT sync event body

sat.invoice.synced is sent by the SAT download service. Its body is the same regardless of the webhook’s payload version:
Compared with resource events, the type is in event, the time is created_at in seconds, and there is no livemode.

data fields

Invoice batch event body

invoice_batch.completed is sent once, when every accepted invoice of an income invoice batch has a final status. Like the SAT event, its body is the same regardless of the webhook’s payload version, the type is in event and created_at is in seconds:

Delivery Behavior

Deliveries go to every active webhook subscribed to the event. Order across events is not guaranteed, and the same event can arrive more than once. Resource events
  • Each attempt times out after 10 seconds.
  • A 2xx response counts as delivered.
  • 408, 429, 5xx, timeouts and network errors are retried with exponential backoff, up to 16 attempts in total.
  • Any other 4xx is final and is not retried, so don’t return a 4xx for a failure you want redelivered.
  • Webhooks are never disabled automatically because of failed deliveries.
sat.invoice.synced and invoice_batch.completed
  • One attempt with a 10-second timeout. It is never retried, and your response is ignored.
A delivery can still be missed, so treat webhooks as notifications and reconcile periodically against the API, for example with GET /payments, GET /invoices/sat for SAT invoices, or GET /invoices/income/batch/{id} for a batch.

Verifying Signatures

Only sat.invoice.synced and invoice_batch.completed deliveries are signed. To authenticate resource events, see Headers. Compute the HMAC over the raw request bytes, before any JSON parsing, and compare it in constant time. The key is the secret string exactly as returned when you created the webhook.

Node.js / Express

This example is a receiver for signed SAT and invoice-batch events only. It verifies the original bytes and commits the event to PostgreSQL before replying. It does not import an invoice or complete your business workflow in the request. Use a separate route with a configured secret header for unsigned resource events; do not disable signature verification to mix the two families. In your own integration project, install express and pg. Set DATABASE_URL to your database, GIGSTACK_WEBHOOK_ID to this webhook’s ID and GIGSTACK_WEBHOOK_SECRET to its creation secret. Keep them in your local secret configuration. Use your database provider’s required TLS settings. Create this inbox table in that database:
Save this as receiver.cjs. The insert runs as one committed PostgreSQL statement; a duplicate delivery returns success without creating another work item.
Start it with node receiver.cjs and register its HTTPS route. This is a complete intake example; you must implement the worker and reconciliation for your product:
  1. Claim unprocessed rows durably, with a database transaction or a queue lease so concurrent workers do not process the same row at once.
  2. For sat.invoice.synced, read the SAT invoice using payload.data.uuid. For invoice_batch.completed, retrieve the batch using payload.data.id and page through its items. Use credentials belonging to the intended team.
  3. Upsert your own records using stable business IDs. Commit your changes and mark processed_at only after success. If your effect is in another system, use that system’s idempotency mechanism and reconcile uncertain outcomes.
  4. On failure, retain the row, record a redacted error, and retry it with a bounded policy. A restart must resume unfinished rows; an in-memory set cannot do this.
  5. Poll the relevant API periodically to discover missed notifications. Signed events are sent once: a 503 from this receiver does not cause gigstack to retry them. Alert on inbox failures and reconcile the gap.
Test intake separately from your worker: send the same correctly signed fixture twice and expect one row; alter its body without recomputing the signature and expect 401; stop the database and expect no 200; restart the worker and confirm unfinished rows are processed. These are integration checks to run in your own environment, not claims that this PostgreSQL example was exercised against a live webhook in the docs audit.

Python / Flask

This is a signature-verification helper, not a complete receiver. Call it on request.get_data() before parsing JSON, then persist/enqueue the verified event before returning 2xx, following the inbox requirements above.

PHP

This helper performs signature verification only. Read the raw request bytes with file_get_contents('php://input'), pass the signature header and stored secret, and persist/enqueue the verified event before acknowledging it.

Resource-event authentication and processing

Resource events have no X-Gigstack-Signature. Configure a secret custom header in the dashboard and compare it in constant time on a separate resource-event route. The header cannot currently be configured through this API. Keep that route closed until the header is configured, and do not accept unsigned requests on the signed route. For a v2 resource event, persist the delivery with its id before acknowledgment, then reconcile data.object with the corresponding GET endpoint. For v1/v1.1, retain each notification with your own inbox ID; the resource identity is not a unique delivery ID. Handle deletion notifications using the last known state and an idempotent local tombstone. Keep the two payload versions’ field names and millisecond timestamps distinct from the signed events’ second-based timestamps.

Testing Webhooks

Using webhook.site

For development and testing, use webhook.site to inspect webhook payloads:

Test events from the dashboard

The Send test event button in the gigstack dashboard posts a sample directly from your browser:
  • Body: your webhook’s format, plus "test": true. A v1 sample leaves out team, webhook and livemode.
  • Headers: X-Gigstack-Test: true. Your custom headers are not included.
  • CORS: your endpoint must allow cross-origin requests for the dashboard to show its response.
Use it to check that your endpoint is reachable. Build your parser from Webhook Payload, not from a test event.

Local Testing with ngrok

  1. Start your local server:
  1. Create an ngrok tunnel:
  1. Use the ngrok HTTPS URL in your webhook:

Best Practices

  1. Subscribe to specific events - Only listen to events you need to reduce noise
  2. Use inactive status for testing - Create webhooks with status: "inactive" to test configuration before enabling
  3. Plan for missed and duplicate deliveries - Resource events are retried but can still arrive more than once or not at all, and sat.invoice.synced and invoice_batch.completed are never retried; de-duplicate, and reconcile periodically against the API
  4. Log webhook events - Keep audit logs of received webhooks for debugging
  5. Monitor webhook performance - Track delivery success rates and response times
  6. Version your webhook endpoints - Use versioned URLs (e.g., /webhooks/v1/gigstack) for easier updates
  7. Handle all event types - Include a default case for unknown event types to future-proof your integration

Error Handling

Errors use the standard error envelope.

Validation Error (400)

Returned when the body is invalid: a malformed url, an unknown event type, or a missing required field. details lists each problem as field: problem.

Webhook Not Found (404)


For additional assistance, contact support@gigstack.io