PPactDocs
Developers

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:

bash
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.

python
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:

bash
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" }'

What's next?