Skip to content

Create an event subscription

POST
https://api.getsitecontrol.com/v1/sites/{site_id}/event-subscriptions
curl --request POST \
--url https://api.getsitecontrol.com/v1/sites/1/event-subscriptions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "name": "example", "events": [ "example" ], "filter": { "widget_ids": [ 1 ] }, "url": "example", "method": "POST", "format": "json", "headers": [ { "name": "example", "value": "example", "is_secret": false } ], "body": "example" }'

Creates an active event subscription and returns it with its signing secret. The secret is shown in this response only: keep it to verify the webhook-signature header (Standard Webhooks, HMAC-SHA256); a lost secret means deleting the event subscription and creating it again. With an Idempotency-Key the stored response, secret included, is kept for 24 hours.

The receiver can be any HTTP API: url, headers and the body template take the request it expects, e.g. a Slack, Telegram or Airtable call.

Event types and their payloads come from list_event_types. The payload follows the request’s GSC-Version, kept as api_version. Only events that happen after creation are sent; earlier data comes from the responses and contacts lists. Before saving, preview_new_event_subscription shows the exact request on a sample. A site holds at most 20 event subscriptions, inactive ones included. Rights follow the data an event subscription sends out: response.created needs the widgets and contacts sections, contact.created the contacts section. Reading needs view of each, changing edit.

FieldDescription
site_id
required
Site Id
integer
FieldDescription
Idempotency-Key
Idempotency-Key

Makes a retry of a POST safe: a repeat with the same key, body and GSC-Version returns the stored response with Idempotency-Replayed: true instead of running again. The key protects against repeated execution while the key store works. Saving the change and storing the response under the key are not atomic: if the process or the key store fails between them, the change is kept, the key is released after 15 minutes and a retry runs the request again. After a timeout or a 5xx, read the resource to check the result before retrying. 5xx and 429 responses and responses over 256 KB are not stored, so a repeat runs the request again. A key is kept for 24 hours. The same key with another body or version is 422; a repeat while the first request runs is 409.

string
>= 1 characters <= 128 characters /^[A-Za-z0-9_-]+$/

Makes a retry of a POST safe: a repeat with the same key, body and GSC-Version returns the stored response with Idempotency-Replayed: true instead of running again. The key protects against repeated execution while the key store works. Saving the change and storing the response under the key are not atomic: if the process or the key store fails between them, the change is kept, the key is released after 15 minutes and a retry runs the request again. After a timeout or a 5xx, read the resource to check the result before retrying. 5xx and 429 responses and responses over 256 KB are not stored, so a repeat runs the request again. A key is kept for 24 hours. The same key with another body or version is 422; a repeat while the first request runs is 409.

Media typeapplication/json
EventSubscriptionCreate
object
FieldDescription
name
Name

Your label for the event subscription, at most 100 characters

string
nullable
events
required
Events

1 or 2 distinct event types from list_event_types: response.created, a widget response became active; contact.created, a contact was created by a widget submit or through the public API

Array<string>
filter
Filter

Which widgets’ responses are sent; every widget by default

object
FieldDescription
widget_ids
Widget Ids

Only responses of these widgets (ids from list_widgets), at most 100; empty means every widget. Only with response.created

Array<integer>
url
required
Url

Receiver URL: https only, at most 2000 characters, a public host. Placeholders such as {{ response.fields.email.value }} are allowed in the path and query, never in the scheme, host or port; their values are percent-encoded. Everyone who may read the event subscription sees the URL in full: put secrets in secret headers, not in the URL

string
method
Method

HTTP method of the request

string
default: POST
Allowed values: POST PUT PATCH
format
Format

Json: an application/json body; form: an application/x-www-form-urlencoded body, the default payload flattened into keys when there is no template

string
default: json
Allowed values: json form
headers
Headers

Up to 20 headers sent with every request. Credentials of the receiver go here as secret headers, e.g. {"name": "Authorization", "value": "Bearer ...", "is_secret": true}; a secret value reads ***

Array<object>
EventSubscriptionHeader
object
FieldDescription
name
required
Name

Header name, e.g. Authorization. Host, Content-Length, Transfer-Encoding, Connection, User-Agent and webhook-* are set by the sender. A Content-Type replaces the one of format

string
value
required
Value

Sent as is, no placeholders; at most 4096 characters, no line breaks. On update, *** in a secret header keeps its stored value

string
is_secret
Is Secret

True for credentials: the value is stored encrypted, reads *** everywhere and is masked in the journal

boolean
body
Body

Null sends the default payload of the event (its schema is in list_event_types). Otherwise a JSON object or array sent instead, its strings filled from the event, e.g. {"text": "New lead {{ response.fields.email.value }} from {{ widget.name }}"}. Roots: event, site, contact; for response.created also response (an answer is response.fields.<name>.value), widget and query. A string that is exactly one placeholder keeps the value’s type (number, boolean, list). Filters: default, join, date, lower, upper, url_encode, e.g. {{ contact.first_name | default: '' }}. Keys with dots or dashes: response.fields['first-name'].value. No placeholders in object keys. With format: form the template is a flat object of scalars. At most 16 KB and 10 levels deep; the sent body at most 64 KB. Check it with preview_new_event_subscription or preview_event_subscription

nullable

Successful Response

Media typeapplication/json
EventSubscriptionCreated
object
FieldDescription
id
required
Id
integer
name
Name

Your label for the event subscription

string
nullable
events
required
Events

Event types it sends

Array<string>
filter
required
EventSubscriptionFilter
object
FieldDescription
widget_ids
Widget Ids

Only responses of these widgets (ids from list_widgets), at most 100; empty means every widget. Only with response.created

Array<integer>
url
required
Url

Receiver URL; placeholders are filled for each event

string
method
required
Method

HTTP method of the request

string
Allowed values: POST PUT PATCH
format
required
Format

Json: an application/json body; form: an application/x-www-form-urlencoded body, the default payload flattened into keys when there is no template

string
Allowed values: json form
headers
required
Headers
Array<object>
EventSubscriptionHeader
object
FieldDescription
name
required
Name

Header name, e.g. Authorization. Host, Content-Length, Transfer-Encoding, Connection, User-Agent and webhook-* are set by the sender. A Content-Type replaces the one of format

string
value
required
Value

Sent as is, no placeholders; at most 4096 characters, no line breaks. On update, *** in a secret header keeps its stored value

string
is_secret
Is Secret

True for credentials: the value is stored encrypted, reads *** everywhere and is masked in the journal

boolean
body
required
Body

Body template; null sends the default payload

nullable
api_version
required
Api Version

Version of the payload sent: the GSC-Version of the request that created the event subscription; changes only with update_event_subscription

string
is_active
required
Is Active

True: sends events; false: sends nothing, and deliveries still waiting are cancelled

boolean
last_success_at
Last Success At

Last real delivery accepted by the receiver; tests not counted

string format: date-time
nullable
last_failure_at
Last Failure At

Last real delivery that failed for good; tests not counted. A failing receiver never disables the event subscription

string format: date-time
nullable
created_at
required
Created At
string format: date-time
updated_at
required
Updated At
string format: date-time
created_by
Created By

Null when created with an API key or by Support

object
FieldDescription
id
Id

Null for Support or for a user who was deleted

integer
nullable
name
Name

Null when the user has no name

string
nullable
email
Email

The user’s sign-in e-mail; null for Support

string
nullable
secret
required
Secret

Signing secret (whsec_...), shown only here: keep it to verify the webhook-signature header

string

Example

{
"method": "POST",
"format": "json",
"headers": [
{
"is_secret": false
}
]
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Idempotency-Replayed
string

True when the response is a stored one, replayed

Unsupported GSC-Version

Media typeapplication/problem+json
Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Missing or invalid API key or access token

Media typeapplication/problem+json
Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

WWW-Authenticate
string

Authentication scheme: Bearer

Access denied

Media typeapplication/problem+json
Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Not found

Media typeapplication/problem+json
Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Media typeapplication/problem+json

event_subscription_limit_reached: the site already holds 20 event subscriptions; delete one first. idempotency_in_progress: a request with the same Idempotency-Key is still running

Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Request body too large

Media typeapplication/problem+json
Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Media typeapplication/problem+json

validation_failed: every problem of the configuration at once, each with its loc in the body and a message, e.g. an unknown event type, a widget not found, a header the sender sets, a URL that is not public https or a template error

Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Rate limited

Media typeapplication/problem+json
Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds

Retry-After
integer

Seconds until the bucket refills

Media typeapplication/problem+json

temporarily_unavailable: secrets, limits or idempotency keys cannot be handled right now; retry later

Problem

The documented shape of every error body on the versioned surface.

object
FieldDescription
type
required
Type
string
title
required
Title
string
status
required
Status
integer
detail
Detail
string
nullable
instance
required
Instance
string
reason
Reason
string
nullable
next
Next
string
nullable
request_id
required
Request Id
string
errors
Errors
Array<object>
nullable
ValidationIssue
object
FieldDescription
loc
required
Loc
Array
msg
required
Msg
string
type
required
Type
string
metadata
Metadata
object

Example · Generated

{
"type": "example",
"title": "example",
"status": 1,
"detail": "example",
"instance": "example",
"reason": "example",
"next": "example",
"request_id": "example",
"errors": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example"
}
],
"metadata": {}
}
FieldDescription
GSC-Version
string

API version the response was produced for

X-Request-Id
string

Request id, echoed from the request when valid

X-RateLimit-Limit
integer

Limit of the tightest bucket

X-RateLimit-Remaining
integer

Requests left in its window

X-RateLimit-Reset
integer

End of its window, epoch seconds