{% set mauticBaseUrl = app.request ? app.request.schemeAndHttpHost : 'https://mautic.example.com' %}

<div class="row">
    <div class="col-md-12">
        <div class="panel panel-default">
            <div class="panel-heading"><strong>Webhook signing</strong></div>
            <div class="panel-body">
                <p>
                    Each inbound webhook source has its own endpoint key and secret under Deal Flow &gt; Inbound Webhooks.
                    Source-specific requests can use either Deal Flow's timestamped signature headers or Mautic's native <code>Webhook-Signature</code> header.
                </p>
                <p>
                    The timestamped format is the hex HMAC SHA-256 of <code>&lt;timestamp&gt;.&lt;raw request body&gt;</code> using the source secret, with a 5 minute tolerance.
                    The native Mautic format is <code>Webhook-Signature: base64(hmac_sha256(raw_body, secret))</code>.
                </p>
                <div class="table-responsive">
                    <table class="table table-striped dealflow-docs-schema">
                        <tbody>
                        <tr>
                            <th>Endpoint</th>
                            <td><code class="dealflow-docs-path">POST /dealflow/webhooks/inbound/{sourceKey}</code></td>
                        </tr>
                        <tr>
                            <th>Headers</th>
                            <td>Use either <code>X-DealFlow-Timestamp</code> + <code>X-DealFlow-Signature</code> or <code>Webhook-Signature</code>.</td>
                        </tr>
                        <tr>
                            <th>Request object</th>
                            <td>
                                Supports <code>deal_id</code> or <code>external_ref</code> for matching; deal fields such as
                                <code>title</code>, <code>company_id</code>, <code>owner_id</code>, <code>stage_id</code>,
                                <code>stage_slug</code>, <code>amount</code>, <code>currency</code>, <code>confidence</code>,
                                <code>expected_close_date</code>, and <code>description</code>; optional <code>company</code> object,
                                <code>contacts</code> array, <code>note_body</code>, <code>meeting_url</code>, <code>task</code> object,
                                <code>source</code>, and <code>source_ref</code>.
                            </td>
                        </tr>
                        <tr>
                            <th>Behavior</th>
                            <td>Matches by <code>deal_id</code>, then <code>external_ref</code>, then by contacts plus company. It creates a deal when no match is found.</td>
                        </tr>
                        <tr>
                            <th>Success response</th>
                            <td>
                                <code>200 OK</code> with <code>{ "deal": { "event": "deal.webhook_processed", ... } }</code>.
                                A <code>409</code> is returned when multiple deals match the incoming contact/company lookup.
                            </td>
                        </tr>
                        </tbody>
                    </table>
                </div>
                <pre class="dealflow-docs-code"><code>{
  "external_ref": "crm-123",
  "title": "Acme Renewal",
  "company": {
    "name": "Acme Corp"
  },
  "owner_id": 7,
  "stage_slug": "proposal",
  "amount": "25000.00",
  "currency": "USD",
  "confidence": 70,
  "expected_close_date": "2026-03-31",
  "description": "Renewal for enterprise plan",
  "contacts": [
    {
      "email": "buyer@example.com",
      "label": "Decision Maker"
    }
  ],
  "note_body": "Discovery completed",
  "meeting_url": "https://meet.example.com/acme-renewal",
  "task": {
    "title": "Send pricing packet",
    "owner_id": 7,
    "due_at": "2026-03-18T15:00:00Z"
  },
  "source": "crm",
  "source_ref": "meeting-8831"
}</code></pre>
                <pre class="dealflow-docs-code"><code>TIMESTAMP=$(date +%s)
BODY='{"external_ref":"crm-123","note_body":"Discovery completed"}'
SIGNATURE=$(printf '%s.%s' "$TIMESTAMP" "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -binary | xxd -p -c 256)

curl -X POST "{{ mauticBaseUrl }}/dealflow/webhooks/inbound/src_example" \
  -H "Content-Type: application/json" \
  -H "X-DealFlow-Timestamp: $TIMESTAMP" \
  -H "X-DealFlow-Signature: $SIGNATURE" \
  -d "$BODY"</code></pre>
                <pre class="dealflow-docs-code"><code>BODY='{"external_ref":"crm-123","note_body":"Discovery completed"}'
SIGNATURE=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -binary | openssl base64)

curl -X POST "{{ mauticBaseUrl }}/dealflow/webhooks/inbound/src_example" \
  -H "Content-Type: application/json" \
  -H "Webhook-Signature: $SIGNATURE" \
  -d "$BODY"</code></pre>
                <pre class="dealflow-docs-code mb-0"><code>{
  "deal": {
    "event": "deal.webhook_processed",
    "occurred_at": "2026-03-11T14:45:00+00:00",
    "origin": "webhook",
    "actor": {
      "type": null,
      "id": null
    },
    "deal": {
      "id": 123,
      "title": "Acme Renewal",
      "status": "open",
      "stage": "Proposal",
      "amount": "25000.0000",
      "currency": "USD",
      "confidence": 70,
      "owner_id": 7,
      "company_id": 42,
      "external_ref": "crm-123",
      "expected_close_date": "2026-03-31",
      "updated_at": "2026-03-11T14:45:00+00:00"
    },
    "company": {
      "id": 42,
      "name": "Acme Corp"
    },
    "contacts": [
      {
        "id": 99,
        "label": "Decision Maker",
        "name": "Pat Buyer",
        "email": "buyer@example.com"
      }
    ]
  },
  "webhook_source": {
    "name": "CRM Sync",
    "source_key": "src_example",
    "signature_format": "mautic_webhook_signature"
  }
}</code></pre>
            </div>
        </div>
    </div>
</div>
