Webhooks
Webhooks can be used to receive realtime updates of changes made on the platform by customers and other users, as well as any automated processes such as payments and shipments. You can subscribe to these events by registering a webhook, which is an endpoint on your system that will receive a HTTP POST with a JSON body containing details of the event. The webhook subscription can specify which fields the webhook should receive using a regular GraphQL subscription query.
See GraphQL Subscriptions for a more general introduction to the concept.
Create a webhook
To create a webhook subscription, use the createWebhookSubscription mutation with the following fields:
- webhookUrl: Url!required
The URL of the endpoint on your server to be notified when the event occurs
- secret: String!required
The secret used to sign the JWT in the webhook request
- query: String!required
The query containing the subscription and fields required by the webhook endpoint
The query will subscribe to one or more event types detailed in GraphqlSubscription using the subscription top level language construct.
Query
mutation($input: CreateWebhookSubscriptionMutationInput!) {
createWebhookSubscription(input: $input) {
webhookSubscription {
id
webhookUrl
query
}
}
}Variables
{
"input": {
"webhookUrl": "http://example.com/wh",
"secret": "very_secret",
"query": "subscription { userCreated { id, emailAddress } }"
}
}Response
{
"data": {
"createWebhookSubscription": {
"webhookSubscription": {
"id": "WebhookSubscription-MEODC8",
"webhookUrl": "http://example.com/wh",
"query": "subscription { userCreated { id, emailAddress } }"
}
}
}
}Now, every time a new user is created, the endpoint at webhookUrl will be sent a request containing the details of the new user, with the fields specified in the query:
Webhook Request
POST http://example.com:80/whHeaders
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJib2R5U2lnbmF0dXJlIjoiZTU3YTQ3MjFmOTlhYjgxNzE3ZTk4OGQ5YzE0YzJhMWExMDdjOTBkODc4ZDRmZjhhMjZjYWNlYzY4MWQzNGUxNyIsImp0aSI6Ik0zT1dDVyJ9.pVYrBrcFp668PbiPGV4nrpBqvXF0priAAjqVVlP_fFM
Content-Type: application/jsonBody
{
"data": {
"userCreated": {
"id": "AccountHolder-42QF3KP37NW",
"emailAddress": "daisy@example.com"
}
},
"metadata": {
"event": {
"id": "WebhookSubscriptionEvent-M3OWCW",
"timestamp": "2021-05-20T07:00:00-05:00"
},
"subscription": {
"id": "WebhookSubscription-MEODC8"
}
}
}Ping
When the subscription is created, or when you call pingWebhookSubscription, the platform will automatically send a ping request to the webhookUrl endpoint, this can help debug connectivity issues.
Webhook Request
POST http://example.com:80/whHeaders
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJib2R5U2lnbmF0dXJlIjoiMDM1NDNmYmFlMTIwY2FiYWNiNDE5ZDQwMTM3YWMzYTliNTQ1YzM0Njc4NmY2ZmE2MzNkOGY4NGQ0ZDEwYzNhMiIsImp0aSI6IjZWS0pDVyJ9.QU8JQpqe4Rm9cWUhdVvuMuE-XsTUbgf_MOM2JdGP3vk
Content-Type: application/jsonBody
{
"ping": true,
"metadata": {
"event": {
"id": "WebhookSubscriptionEvent-6VKJCW",
"timestamp": "2021-05-20T07:00:00-05:00"
},
"subscription": {
"id": "WebhookSubscription-MEODC8"
}
}
}To check the result of a ping, you can query the webhook subscription:
Query
query($id: ID!) {
webhookSubscription(id: $id) {
id
webhookUrl
events {
nodes {
name
timestamp
webhookResponse {
status
error
}
}
}
}
}Variables
{
"id": "WebhookSubscription-MEODC8"
}Response
{
"data": {
"webhookSubscription": {
"id": "WebhookSubscription-MEODC8",
"webhookUrl": "http://example.com/wh",
"events": {
"nodes": [
{
"name": "ping",
"timestamp": "2021-05-20T07:00:00-05:00",
"webhookResponse": {
"status": 200,
"error": null
}
},
{
"name": "userCreated",
"timestamp": "2021-05-20T07:00:00-05:00",
"webhookResponse": {
"status": 200,
"error": null
}
}
]
}
}
}
}As you can see, the response also contains other events sent to that subscription. These events are accessible for up to 7 days.
Security
When the platform sends a request to a webhook endpoint, it also sends an Authorization header that contains a JWT with some details that you can use to ensure that the request did in fact come from the platform, and wasn't generated by another entity on the internet.
The token is signed using secret that was used in the mutation to create subscription. Once the identity of token is verified, you can additionally check that the event request body hasn't been tampered with using bodySignature of token payload. bodySignature contains SHA256 digest of the event request body.
Here is an example of a webhook request.
JWT from Authorization header:
eyJhbGciOiJIUzI1NiJ9.eyJib2R5U2lnbmF0dXJlIjoiMTBhZDk1M2YwY2VlODhkMDk1MjZmNmI5OWE5ZjZhMzI1MDBhNjgyN2NiMzEzMTliNzMzMTM4YmI5MWU0MTc3YyIsImp0aSI6Ik1EQjNDVyJ9.ozHNUVk6LCCmcdUDoB6MPFZOUHNShaPPF37C88PVp1g
And the following request body:
"{\"data\":{\"userCreated\":{\"id\":\"User-42QF3KP37NW\",\"emailAddress\":\"daisy@example.com\"}},\"metadata\":{\"event\":{\"id\":\"WebhookSubscriptionEvent-MDB3CW\",\"timestamp\":\"2021-03-22T11:26:11Z\"},\"subscription\":{\"id\":\"WebhookSubscription-6L78CB\"}}}"
When the token is decoded, you get the following token payload:
{
"bodySignature": "10ad953f0cee88d09526f6b99a9f6a32500a6827cb31319b733138bb91e4177c",
"jti": "MDB3CW"
}
Now you can generate request body SHA256 digest and it should be identical to the above bodySignature. As long as you verified the token's signature with very_secret secret (the one that's been used in the createWebhookSubscription mutation), this is a proof that the request body hasn't been tampered with.