Install
$ agentstack add skill-impertio-studio-speckle-claude-skill-package-speckle-syntax-webhooks ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
speckle-syntax-webhooks
Quick Reference
Webhook CRUD Operations
| Operation | Mutation | Input Type | Returns | Required Role | |-----------|----------|-----------|---------|---------------| | Create | webhookCreate | WebhookCreateInput! | String! (webhook ID) | Stream Owner | | Update | webhookUpdate | WebhookUpdateInput! | String! | Stream Owner | | Delete | webhookDelete | WebhookDeleteInput! | String! | Stream Owner | | Query | project.webhooks | id: String (optional filter) | WebhookCollection | Stream Owner |
WebhookCreateInput Type
| Field | Type | Required | Description | |-------|------|----------|-------------| | streamId | String! | YES | The project/stream ID | | url | String! | YES | HTTPS endpoint URL that receives POST requests | | description | String | NO | Human-readable description | | triggers | [String!]! | YES | Array of trigger strings (MUST use internal names) | | secret | String | NO | Shared secret for HMAC signature verification | | enabled | Boolean | NO | Defaults to true |
All 14 Event Types (Trigger Strings)
ALWAYS use the Internal Trigger String when calling webhookCreate. NEVER use the Display Name.
| Internal Trigger String | Display Name | Description | |------------------------|--------------|-------------| | stream_update | projectupdate | Project metadata changed | | stream_delete | projectdelete | Project deleted | | branch_create | modelcreate | New model created | | branch_update | modelupdate | Model metadata updated | | branch_delete | modeldelete | Model deleted | | commit_create | versioncreate | New version created | | commit_update | versionupdate | Version metadata updated | | commit_receive | versionreceive | Version marked as received | | commit_delete | versiondelete | Version deleted | | comment_created | commentcreated | New comment/thread created | | comment_archived | commentarchived | Comment archived | | comment_replied | commentreplied | Reply added to comment thread | | stream_permissions_add | projectpermissionsadd | Collaborator added | | stream_permissions_remove | projectpermissionsremove | Collaborator removed |
Configuration Limits
| Limit | Value | |-------|-------| | Max webhooks per stream/project | 100 (MAX_STREAM_WEBHOOKS = 100) | | Webhook history query limit | Up to 25 events per query | | Required scope | streams:write | | Required role | Stream Owner |
Critical Warnings
NEVER use display names (e.g., version_create, model_update) as trigger strings -- the API uses legacy/internal terminology. ALWAYS use commit_create, branch_update, etc. The UI shows modern names, but the GraphQL API requires legacy names.
NEVER assume the webhook secret is returned in queries -- Speckle exposes only a hasSecret boolean field. The actual secret value is NEVER returned after creation. Store it securely at creation time.
NEVER skip payload validation in your webhook endpoint -- ALWAYS verify the webhook.id in the payload matches your expected webhook. Without validation, any POST to your endpoint could be processed as a legitimate event.
NEVER exceed 100 webhooks per project -- the server enforces MAX_STREAM_WEBHOOKS = 100. Attempting to create more will fail. If you need more event handlers, consolidate triggers into fewer webhooks.
NEVER expect sensitive fields in webhook payloads -- server.id, user.passwordDigest, user.email, and webhook.secret are ALWAYS stripped before delivery.
Webhook Lifecycle
1. Create a Webhook
mutation WebhookCreate($webhook: WebhookCreateInput!) {
webhookCreate(webhook: $webhook)
}
Variables:
{
"webhook": {
"streamId": "",
"url": "https://your-endpoint.com/webhook",
"description": "CI/CD trigger on new versions",
"triggers": ["commit_create", "branch_update"],
"secret": "your-hmac-secret-here",
"enabled": true
}
}
Returns the webhook ID as String!. Store this ID for future update/delete operations.
2. Update a Webhook
mutation WebhookUpdate($webhook: WebhookUpdateInput!) {
webhookUpdate(webhook: $webhook)
}
Variables:
{
"webhook": {
"id": "",
"streamId": "",
"url": "https://new-endpoint.com/webhook",
"enabled": false
}
}
All fields except id and streamId are optional -- only include fields you want to change.
3. Delete a Webhook
mutation WebhookDelete($webhook: WebhookDeleteInput!) {
webhookDelete(webhook: $webhook)
}
Variables:
{
"webhook": {
"id": "",
"streamId": ""
}
}
4. Query Webhooks
query ProjectWebhooks($projectId: String!) {
project(id: $projectId) {
webhooks {
totalCount
items {
id
url
description
triggers
enabled
hasSecret
history(limit: 5) {
totalCount
items {
id
status
statusInfo
retryCount
lastUpdate
}
}
}
}
}
}
Filter for a specific webhook: webhooks(id: "").
Payload Structure
When a webhook fires, the receiving endpoint gets a POST request with this JSON body:
{
"streamId": "",
"stream": {
"id": "abc123",
"name": "My Project",
"description": "Project description"
},
"userId": "",
"user": {
"id": "user123",
"name": "Jane Doe",
"bio": "Engineer",
"company": "ACME",
"avatar": "https://..."
},
"server": {
"name": "Speckle",
"canonicalUrl": "https://app.speckle.systems"
},
"webhook": {
"id": "webhook123",
"streamId": "abc123",
"url": "https://your-endpoint.com/webhook",
"description": "CI/CD trigger",
"triggers": ["commit_create"]
},
"event": {
"event_name": "commit_create",
"data": { }
}
}
Stripped Fields (Security)
The following fields are ALWAYS removed from payloads before delivery:
server.iduser.passwordDigestuser.emailwebhook.secret
Security: HMAC Signature Verification
When you provide a secret during webhook creation, Speckle signs the payload using HMAC. Your endpoint MUST verify this signature to ensure the request is authentic.
Verification Pattern
- Extract the signature from the request headers
- Compute HMAC-SHA256 of the raw request body using your stored secret
- Compare the computed signature with the received signature using constant-time comparison
- Reject requests where signatures do not match
import hmac
import hashlib
def verify_webhook_signature(payload_body: bytes, secret: str, received_signature: str) -> bool:
"""Verify the HMAC signature of a Speckle webhook payload."""
computed = hmac.new(
secret.encode("utf-8"),
payload_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(computed, received_signature)
ALWAYS use constant-time comparison (hmac.compare_digest) -- NEVER use == for signature comparison, as it is vulnerable to timing attacks.
Webhook History and Retry Behavior
Querying Delivery History
Each webhook maintains a delivery history accessible via webhook.history(limit: N):
{
project(id: $projectId) {
webhooks(id: $webhookId) {
items {
history(limit: 25) {
totalCount
items {
id
webhookId
status
statusInfo
retryCount
lastUpdate
payload
}
}
}
}
}
}
WebhookEvent Fields
| Field | Type | Description | |-------|------|-------------| | id | String! | Unique event ID | | webhookId | String! | Parent webhook ID | | status | Int! | HTTP response status code from your endpoint | | statusInfo | String! | Additional status information or error message | | retryCount | Int! | Number of delivery attempts made | | lastUpdate | DateTime! | Timestamp of the last delivery attempt | | payload | String! | The JSON payload that was sent |
Retry Behavior
When a webhook delivery fails (non-2xx response or connection error), Speckle retries the delivery. The retryCount field on WebhookEvent tracks how many attempts have been made. Monitor webhook.history to detect persistent delivery failures and take corrective action.
ALWAYS return a 2xx HTTP status code from your webhook endpoint promptly -- long-running processing MUST be deferred to a background queue. A slow response or timeout triggers retries and may cause duplicate processing.
Webhook Integration Pattern
The standard pattern for integrating with Speckle webhooks:
1. Create webhook via webhookCreate mutation (store the returned ID)
2. Receive POST requests at your endpoint
3. Validate the payload:
a. Verify HMAC signature (if secret was configured)
b. Confirm webhook.id matches your expected webhook
4. Extract event data from the payload
5. Return 2xx immediately
6. Process the event asynchronously (background job/queue)
7. Query additional details via GraphQL if needed
Collection Types
| Type | Fields | Access Pattern | |------|--------|---------------| | WebhookCollection | items: [Webhook!]!, totalCount: Int! | project.webhooks | | WebhookEventCollection | items: [WebhookEvent], totalCount: Int! | webhook.history(limit: N) |
Reference Links
- [references/methods.md](references/methods.md) -- GraphQL mutations, input types, and query fields for webhooks
- [references/examples.md](references/examples.md) -- Complete working examples for webhook CRUD and payload handling
- [references/anti-patterns.md](references/anti-patterns.md) -- Common webhook configuration mistakes and how to avoid them
Official Sources
- Speckle Server GitHub -- Webhooks Schema:
github.com/specklesystems/speckle-server/.../webhooks.graphql - Speckle Server GitHub -- Webhook Services:
github.com/specklesystems/speckle-server/.../webhooks/services/webhooks.ts - Speckle Server GitHub -- Webhook Types:
github.com/specklesystems/speckle-server/.../webhooks/domain/types.ts - Speckle Frontend -- Webhook Composables:
github.com/specklesystems/speckle-server/.../webhooks.ts
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Speckle-Claude-Skill-Package
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.