Webhooks let you receive events from Productlane as they happen - new threads, status changes, comments, contacts, changelogs, and more - without polling list endpoints. Every delivery is signed with HMAC-SHA256 so you can verify it came from us.Documentation Index
Fetch the complete documentation index at: https://productlane.mintlify.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
How it works
- You register a webhook with a
url, alabel, and a list of event patterns. - We give you back a
secret. Keep it - you’ll need it to verify deliveries. - When an event happens, we POST a JSON payload to your URL.
- If your endpoint responds with
2xx, the delivery succeeds. Otherwise we retry on a schedule (1m, 5m, 30m, 2h, 6h, 24h) before marking the deliverydead.
Subscribe to events
The picker exposes 12 resource-level patterns. Subscribe to the resource, then switch on thetype field at delivery time.
| Pattern | Covers |
|---|---|
thread.* | thread.created, thread.updated, thread.deleted, thread.status_changed, thread.assigned, thread.tagged |
message.* | message.received (inbound), message.sent (outbound) |
comment.* | comment.created, comment.updated, comment.deleted |
contact.* | contact.created, contact.updated, contact.deleted |
company.* | company.created, company.updated, company.deleted |
changelog.* | changelog.created, changelog.published, changelog.updated, changelog.deleted |
issue.* | issue.created, issue.updated, issue.completed, issue.canceled, issue.deleted |
project.* | project.created, project.updated, project.completed, project.canceled, project.deleted |
customer_need.* | customer_need.created, customer_need.deleted (thread ↔ project/issue links) |
doc.* | doc.created, doc.updated, doc.deleted, doc.published |
tag.* | tag.created, tag.updated, tag.deleted |
membership.* | membership.invited, membership.joined, membership.role_changed, membership.removed |
thread.created and silently miss thread.assigned. Subscribe to the resource, switch on type in your handler.
Delivery payload
Every delivery is a POST with this body:Productlane-Event-Idis stable across retries. If you process the sameevent_idtwice, drop the second. (Most deliveries fire once, but retries can produce duplicates after network hiccups.)Productlane-Delivery-Idis unique per HTTP attempt and useful for matching against the delivery log in the dashboard.
Verify the signature
The signature is HMAC-SHA256 over${timestamp}.${rawBody}, using your webhook’s secret as the key.
Always read the request body raw before parsing it as JSON. Re-serializing reorders keys and breaks the signature.
Node.js
Python
Retries and delivery state
| Status | Meaning |
|---|---|
pending | Queued for delivery, hasn’t been attempted yet. |
succeeded | We got a 2xx from your endpoint. |
failed | Non-2xx, network error, or your endpoint timed out. Retry scheduled. |
dead | All 6 attempts failed, or the webhook was deactivated mid-flight. |
dead. We keep the delivery log for 30 days at Settings → Integrations → API → Webhooks → [webhook] → Deliveries - you can inspect the response body, status, headers, and replay it manually from there.
Receiver checklist
Things receivers commonly get wrong, in order of how often they bite us in support:- Respond fast. Acknowledge with
2xxwithin 10 seconds. Do the actual work in a background job. - Read the body raw. Frameworks that auto-parse JSON usually destroy whitespace and break the signature. Express:
express.raw({ type: "application/json" })before your JSON middleware. - Verify the signature. Always. Don’t trust IP allowlists alone.
- Deduplicate by
Productlane-Event-Id. Retries can re-deliver successful events if your endpoint replied slowly the first time. - Reject stale deliveries. If the signature timestamp is older than 5 minutes, return
400- it’s an attempted replay. - Return 410 for permanently-gone endpoints. We don’t auto-disable webhooks based on status codes (yet), but the dashboard surfaces 410s prominently.