Sync Pact deals with project tickets via webhooks
Create a ticket in a popular project tool whenever a deal reaches a stage, using webhooks.
When a deal hits "Closed Won," your delivery team usually needs a ticket in a popular project tool. This recipe wires that up with a webhook so it happens automatically, with no polling.
Estimated time: ~45 minutes.
1 — Subscribe to deal events
Create a webhook subscription pointed at your integration endpoint, scoped to deal events:
curl -X POST https://app.pact.place/v1/webhooks/subscriptions \
-H "Authorization: Bearer $PACT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Deal → project tool",
"url": "https://hooks.example.com/pact/deals",
"event_types": ["deal.won", "deal.stage_changed"]
}'
Save the signing secret from the response.
2 — Receive, verify, and create the ticket
Verify the signature (see the webhook quickstart), then call your project tool's API. Acknowledge fast — do the outbound call after returning 200, or push it onto a queue.
from flask import Flask, request, abort
import os, requests
from verify import verify # the helper from the webhook quickstart
app = Flask(__name__)
SECRET = os.environ["PACT_WEBHOOK_SECRET"]
TOOL_TOKEN = os.environ["PROJECT_TOOL_TOKEN"]
@app.post("/pact/deals")
def on_deal():
raw = request.get_data()
if not verify(SECRET, raw, request.headers.get("X-Pact-Signature", "")):
abort(401)
if request.headers.get("X-Pact-Event-Type") != "deal.won":
return "", 200 # only act on won deals
deal = request.get_json()["payload"]
requests.post(
"https://api.projecttool.example/v1/issues",
json={
"title": f"Onboard {deal.get('name', 'new customer')}",
"body": f"Deal {deal['public_id']} closed won. Kick off delivery.",
"labels": ["onboarding"],
},
headers={"Authorization": f"Bearer {TOOL_TOKEN}"},
timeout=10,
)
return "", 200
Deduplicate on the event id
Pact retries failed deliveries, so the same event can arrive more than once. Record the
X-Pact-Event-Id you've already processed and skip duplicates, so you don't create two tickets.
3 — Close the loop (optional)
When the ticket is done, write the outcome back to the deal so reps see delivery status in Pact:
curl -X PATCH https://app.pact.place/v1/opportunities/$DEAL_PUBLIC_ID \
-H "Authorization: Bearer $PACT_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "onboarding_status": "complete" }'