curl --request POST \
--url https://api.gigstack.io/v2/webhooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "https://your-domain.com/webhooks/gigstack",
"events": [
"payment.created",
"payment.succeeded"
],
"description": "Production webhook for payment events",
"status": "active"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://your-domain.com/webhooks/gigstack',
events: ['payment.created', 'payment.succeeded'],
description: 'Production webhook for payment events',
status: 'active'
})
};
fetch('https://api.gigstack.io/v2/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.gigstack.io/v2/webhooks"
payload = {
"url": "https://your-domain.com/webhooks/gigstack",
"events": ["payment.created", "payment.succeeded"],
"description": "Production webhook for payment events",
"status": "active"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"message": "Webhook created successfully. Save the secret — it will not be shown again.",
"data": {
"id": "wh_dyS2ZVTj",
"url": "https://your-domain.com/webhooks/gigstack",
"events": [
"payment.created",
"payment.succeeded"
],
"status": "active",
"description": "Production webhook for payment events",
"owner": "8UWdgXELUhf022vuoq249mtGytG2",
"created_at": 1709090576567,
"secret": "3f9a1c0e7b2d4f6a8c1e3b5d7f9a2c4e6b8d0f1a3c5e7b9d2f4a6c8e0b1d3f5a"
},
"timestamp": 1709090576600
}Create webhook
Create a new webhook endpoint to receive event notifications.
The response includes the webhook’s signing secret. It is shown only once, so store it
immediately. It signs only sat.invoice.synced and invoice_batch.completed deliveries, in the
X-Gigstack-Signature header (sha256= + hex HMAC-SHA256 of the raw body). Resource events are not
signed with it. There is
no endpoint to reveal or rotate the secret later; if you lose it, delete the webhook and create
a new one.
Webhooks created here send resource events in the v1 format unless your team’s default is
v2. Delivery formats, headers and retry behavior are described in the webhookEvent callback
below.
gigstack Connect: Create webhooks for other teams using the team parameter.
curl --request POST \
--url https://api.gigstack.io/v2/webhooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "https://your-domain.com/webhooks/gigstack",
"events": [
"payment.created",
"payment.succeeded"
],
"description": "Production webhook for payment events",
"status": "active"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://your-domain.com/webhooks/gigstack',
events: ['payment.created', 'payment.succeeded'],
description: 'Production webhook for payment events',
status: 'active'
})
};
fetch('https://api.gigstack.io/v2/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.gigstack.io/v2/webhooks"
payload = {
"url": "https://your-domain.com/webhooks/gigstack",
"events": ["payment.created", "payment.succeeded"],
"description": "Production webhook for payment events",
"status": "active"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"success": true,
"message": "Webhook created successfully. Save the secret — it will not be shown again.",
"data": {
"id": "wh_dyS2ZVTj",
"url": "https://your-domain.com/webhooks/gigstack",
"events": [
"payment.created",
"payment.succeeded"
],
"status": "active",
"description": "Production webhook for payment events",
"owner": "8UWdgXELUhf022vuoq249mtGytG2",
"created_at": 1709090576567,
"secret": "3f9a1c0e7b2d4f6a8c1e3b5d7f9a2c4e6b8d0f1a3c5e7b9d2f4a6c8e0b1d3f5a"
},
"timestamp": 1709090576600
}Authorizations
Authentication Method: HTTP Bearer token.
The runtime requires the literal Bearer prefix — a bare token in the
Authorization header is rejected with 401 unauthorized.
Header Format: Authorization: Bearer YOUR_API_KEY
Your API key is a JWT. Live keys operate on live data (livemode: true);
test keys operate on isolated test data (livemode: false).
Get your key at: app.gigstack.pro/settings?tab=api
Errors: credential failures are answered by the authentication layer with a raw
{ "message": … } body, not the standardized envelope — 401 for a missing, malformed or
expired token, 403 for a revoked key or a plan without API access. See the Unauthorized
and AuthForbidden responses.
Query Parameters
gigstack Connect: Target team ID for multi-team access.
Requires gigstack Connect enabled on your team and shared billing account.
Also requires the multipleIssuerAccounts feature on your plan. Requests targeting a
team other than the one your API key belongs to return 403 without it.
Only API keys can use it: an OAuth access token sent with another team's id is rejected with
403 Team mismatch with OAuth token.
Optional — omit it entirely unless you are acting on another team. It deliberately
carries no example value so generated snippets do not emit ?team=undefined; when the
parameter is absent, the team is derived from your API key.
Example: ?team=team_xyz789
Body
Unknown top-level keys are rejected (400 validation_failed / unexpected_key) — body
validation runs in strict allowlist mode. team, livemode and owner are reserved
and injected by the auth middleware.
Endpoint URL that receives the events. Any valid URL is accepted; use HTTPS in production
"https://your-domain.com/webhooks/gigstack"
Array of event types to subscribe to (at least one required)
1payment.created, payment.updated, payment.succeeded, payment.canceled, payment.deleted, payment.upcoming_due_date, invoice.created, invoice.canceled, invoice.failed, invoice_batch.completed, receipt.created, receipt.updated, receipt.completed, receipt.deleted, customer.created, customer.updated, customer.deleted, service.created, service.updated, service.deleted, sat.invoice.synced ["payment.created", "payment.succeeded"]
Optional description of the webhook purpose
"Production webhook for payment events"
Webhook status - defaults to active
active, inactive, null "active"
Callbacks
POST{$request.body#/url}webhookEvent
Body
- Option 1
- Option 2
- Option 3
- Option 4
Body of a webhook delivery (Content-Type: application/json). There are three formats:
- Resource events (
payment.*,invoice.*,receipt.*,customer.*,service.*) use the webhook's payload version:WebhookPayloadV1: the default for webhooks created withPOST /webhooks.WebhookPayloadV2: for webhooks created or last saved in the gigstack dashboard, or on teams whose default is v2.
sat.invoice.syncedalways usesSatInvoiceSyncedWebhookEvent.invoice_batch.completedalways usesInvoiceBatchCompletedWebhookEvent.
Tell them apart by shape:
- v2 has
typeanddata.object. - v1 has
event,teamandwebhook. - The SAT and batch events have
eventandcreated_at; readeventto tell them apart.
Event type a webhook can subscribe to.
payment.created, payment.updated, payment.succeeded, payment.canceled, payment.deleted, payment.upcoming_due_date, invoice.created, invoice.canceled, invoice.failed, invoice_batch.completed, receipt.created, receipt.updated, receipt.completed, receipt.deleted, customer.created, customer.updated, customer.deleted, service.created, service.updated, service.deleted, sat.invoice.synced "sat.invoice.synced"
Team that owns the webhook.
"team_1234567890"
ID of the webhook receiving this delivery.
"wh_dyS2ZVTj"
false for test-mode resources.
true
The resource at the moment of the event, in gigstack's internal (camelCase) format.
metadata values are sent as strings.
Only on deliveries to a gigstack Connect master team. The connected team the event came from.
Only with connectedTeam, when that team has metadata. Values are strings.
Show child attributes
Show child attributes
Response
Any 2xx acknowledges the delivery.
- For resource events,
408,429and5xxtrigger a retry, and any other4xxis final. - For
sat.invoice.syncedandinvoice_batch.completedthe response is ignored.
Response
Webhook created successfully