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
limit(integer, 1-100) - Number of results to return (default: 10)status(string) - Filter by status:activeorinactiveteam(string) - gigstack Connect: Target team ID
Get Webhook
Create Webhook
url(string) - Endpoint URL that receives the events. Any valid URL is accepted; use HTTPS in productionevents(array) - List of event types to subscribe to (see Available Events below)
description(string) - Human-readable description of the webhookstatus(string) -activeorinactive(default:active)
Storesecretnow. It is returned only in this response —GET,PUTand list calls never include it, and it cannot be revealed or rotated later. It signssat.invoice.syncedandinvoice_batch.completeddeliveries only (see Verifying Signatures); resource events such aspayment.succeededare not signed with it. If you lose it, delete the webhook and create a new one.
Update Webhook
Delete Webhook
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.syncedalways uses its own body. It is signed with the webhook’ssecretand is sent only once.invoice_batch.completedalways uses its own body, and is delivered likesat.invoice.synced: signed and sent only once. Despite the prefix, it is not aninvoice.*resource event.
Webhook Structure
Webhook Object
Webhook Payload
Each delivery is an HTTPPOST 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
v2body hastypeanddata.object. - A
v1body hasevent,teamandwebhook.
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:
- Add a secret header to the webhook in the dashboard, for example
Authorization: Bearer <random token>. - Reject any request that doesn’t carry it.
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
2xxresponse counts as delivered. 408,429,5xx, timeouts and network errors are retried with exponential backoff, up to 16 attempts in total.- Any other
4xxis final and is not retried, so don’t return a4xxfor 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.
GET /payments, GET /invoices/sat for SAT invoices, or GET /invoices/income/batch/{id} for a batch.
Verifying Signatures
Onlysat.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, installexpress 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:
receiver.cjs. The insert runs as one committed PostgreSQL statement;
a duplicate delivery returns success without creating another work item.
node receiver.cjs and register its HTTPS route. This is a complete
intake example; you must implement the worker and reconciliation for your product:
- Claim unprocessed rows durably, with a database transaction or a queue lease so concurrent workers do not process the same row at once.
- For
sat.invoice.synced, read the SAT invoice usingpayload.data.uuid. Forinvoice_batch.completed, retrieve the batch usingpayload.data.idand page through its items. Use credentials belonging to the intended team. - Upsert your own records using stable business IDs. Commit your changes and mark
processed_atonly after success. If your effect is in another system, use that system’s idempotency mechanism and reconcile uncertain outcomes. - 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.
- Poll the relevant API periodically to discover missed notifications. Signed
events are sent once: a
503from this receiver does not cause gigstack to retry them. Alert on inbox failures and reconcile the gap.
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 onrequest.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 withfile_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 noX-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. Av1sample leaves outteam,webhookandlivemode. - 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.
Local Testing with ngrok
- Start your local server:
- Create an ngrok tunnel:
- Use the ngrok HTTPS URL in your webhook:
Best Practices
- Subscribe to specific events - Only listen to events you need to reduce noise
- Use inactive status for testing - Create webhooks with
status: "inactive"to test configuration before enabling - Plan for missed and duplicate deliveries - Resource events are retried but can still arrive more than once or not at all, and
sat.invoice.syncedandinvoice_batch.completedare never retried; de-duplicate, and reconcile periodically against the API - Log webhook events - Keep audit logs of received webhooks for debugging
- Monitor webhook performance - Track delivery success rates and response times
- Version your webhook endpoints - Use versioned URLs (e.g.,
/webhooks/v1/gigstack) for easier updates - Handle all event types - Include a default case for unknown event types to future-proof your integration
Related Resources
- Payments API - Process payments that trigger webhook events
- Invoices API - Create invoices that generate webhook events
- Receipts API - Manage receipts with webhook notifications
- Clients API - Customer management with webhook events
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