Webhook Subscription Management
This guide explains how to create and remove webhook subscriptions. HappyColis supports two authentication modes — Integration App tokens and OAuth Client tokens — each with its own set of mutations.
Authentication Modes
Integration App (INTEGRATION)
An Integration App uses a dedicated app token obtained via your client_id / client_secret. You must have the edit_integrations scope to manage webhooks.
OAuth Client (OAUTH_CLIENT)
An OAuth client uses a short-lived access token obtained via the OAuth 2.0 client credentials flow. No extra scope is required beyond having a valid token.
The IntegrationType stored on each subscription (INTEGRATION or OAUTH_CLIENT) determines which removal mutation you must use. You can only remove subscriptions created by your own integration/client.
Creating a Subscription
Input type: IntegrationWebhookInput
| Field | Type | Required | Description |
|---|---|---|---|
endPoint | String (URL) | Yes | HTTPS URL that will receive POST requests |
type | String (WebhookType) | No | Event type to subscribe to (e.g. order/created) |
headers | JSONObject | No | Custom HTTP headers sent with every delivery |
id | ID | No | Reserved — not used on create |
integrationId | ID | No | Reserved — not used on create |
endPointmust be a valid URL. Customheadersare forwarded verbatim alongsideContent-Type: application/jsonand the signature header.
For Integration Apps — integrationWebhookCreate
Required scope: edit_integrationsToken type: Integration App token
mutation IntegrationWebhookCreate($input: IntegrationWebhookInput!) {
integrationWebhookCreate(input: $input) {
id
type
health
endPoint
}
}Variables:
{
"input": {
"endPoint": "https://your-app.com/webhooks/happycolis",
"type": "order/created",
"headers": {
"X-Secret-Token": "my-secret"
}
}
}curl:
curl -X POST https://api-v3.happycolis.com/graphql \
-H "Authorization: Bearer $INTEGRATION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation IntegrationWebhookCreate($input: IntegrationWebhookInput!) { integrationWebhookCreate(input: $input) { id type health endPoint } }",
"variables": {
"input": {
"endPoint": "https://your-app.com/webhooks/happycolis",
"type": "order/created"
}
}
}'For OAuth Clients — oauthWebhookCreate
Token type: OAuth Client Credentials token
mutation OauthWebhookCreate($input: IntegrationWebhookInput!) {
oauthWebhookCreate(input: $input) {
id
type
health
endPoint
}
}Variables:
{
"input": {
"endPoint": "https://your-app.com/webhooks/happycolis",
"type": "shipment/created"
}
}curl:
curl -X POST https://api-v3.happycolis.com/graphql \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation OauthWebhookCreate($input: IntegrationWebhookInput!) { oauthWebhookCreate(input: $input) { id type health endPoint } }",
"variables": {
"input": {
"endPoint": "https://your-app.com/webhooks/happycolis",
"type": "shipment/created"
}
}
}'Node.js example:
const createWebhook = async (accessToken, endPoint, type) => {
const response = await fetch('https://api-v3.happycolis.com/graphql', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
mutation OauthWebhookCreate($input: IntegrationWebhookInput!) {
oauthWebhookCreate(input: $input) {
id
type
health
endPoint
}
}
`,
variables: { input: { endPoint, type } },
}),
});
const { data, errors } = await response.json();
if (errors) throw new Error(errors[0].message);
return data.oauthWebhookCreate;
};Removing a Subscription
Pass the id returned by the create mutation. You can only remove subscriptions that belong to the calling integration or OAuth client.
For Integration Apps — integrationWebhookRemove
mutation IntegrationWebhookRemove($id: String!) {
integrationWebhookRemove(id: $id) {
id
type
endPoint
}
}Variables:
{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }Errors:
| Condition | Error |
|---|---|
| Webhook not found | FORBIDDEN: You can't remove this webhook: webhook not found |
| Webhook belongs to a different integration | FORBIDDEN: You can't remove this webhook: integrationId or integrationType mismatch |
For OAuth Clients — oauthWebhookRemove
mutation OauthWebhookRemove($id: String!) {
oauthWebhookRemove(id: $id) {
id
type
endPoint
}
}Variables:
{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" }Errors:
| Condition | Error |
|---|---|
| Webhook not found | FORBIDDEN: You can't remove this webhook: webhook not found |
| Webhook belongs to a different OAuth client | FORBIDDEN: You can't remove this webhook: integrationId or integrationType mismatch |
Webhook Health States
Each subscription has a health field that reflects delivery status.
| Value | Description |
|---|---|
HEALTHY | All recent deliveries succeeded. The queue is active. |
FAILING | One or more deliveries have failed. The queue is blocked and no further messages will be delivered to this endpoint until the issue is resolved. |
When a queue enters the
BLOCKEDstate, new events are still accepted into the queue but delivery is paused. Check your endpoint for connectivity issues and contact support if the health does not recover.
Uniqueness Constraint
Each integration may only have one active subscription per event type. Attempting to create a duplicate subscription (same integrationId + type) will result in a conflict error from the database.
Integration vs OAuth Mode Comparison
| Feature | Integration App | OAuth Client |
|---|---|---|
| Mutation (create) | integrationWebhookCreate | oauthWebhookCreate |
| Mutation (remove) | integrationWebhookRemove | oauthWebhookRemove |
| Required scope | edit_integrations | (none beyond valid token) |
integrationType stored | INTEGRATION | OAUTH_CLIENT |
| Token guard | AppPayloadGuard | ClientPayloadGuard |
| Can remove other's webhooks | No | No |
See Also
- Overview — system architecture and available events
- Signature Verification — verify incoming webhook requests