# Frappe → Missio Webhook Setup

Six webhooks connect Frappe → Missio to sync cancellations and field updates back after a doc changes in Frappe. All six must be configured in **Integrations → Webhook → New** in Frappe.

## Shared settings (apply to all six)

| Field | Value |
|---|---|
| **Enable Security** | ✓ checked |
| **Webhook Secret** | (same value for all six) — get the latest from Missio at `/account/frappe-settings` → "Regenerate Webhook Secret" |
| **Request Method** | POST |
| **Request Structure** | JSON |
| **Request Timeout** | 5 |
| **Headers** | `Content-Type: application/json` only (delete any auto-added `X-Frappe-Webhook-Signature` custom header — Frappe generates one automatically when Enable Security is checked) |

Replace `https://dev.missio.io` with your actual environment URL (QA, production, local).

## 1. Missio JE Cancel Sync (existing)

- **DocType:** Journal Entry
- **Doc Event:** on_cancel
- **Request URL:** `https://dev.missio.io/webhook/frappe/journal-entry`
- **JSON Request Body:**
  ```json
  {
    "name": "{{ doc.name }}",
    "company": "{{ doc.company }}",
    "docstatus": {{ doc.docstatus if doc.docstatus is defined else 0 }}
  }
  ```

## 2. Missio Account Update Sync (existing)

- **DocType:** Account
- **Doc Event:** on_update
- **Request URL:** `https://dev.missio.io/webhook/frappe/account`
- **JSON Request Body:** same fields as above (add `disabled` + `freeze_account` if needed — Missio's handler uses the cache-bust approach, so just `name` + `company` is fine).

## 3. Missio Budget Update Sync (existing)

- **DocType:** Budget
- **Doc Event:** on_update
- **Request URL:** `https://dev.missio.io/webhook/frappe/budget`
- **JSON Request Body:**
  ```json
  { "name": "{{ doc.name }}", "company": "{{ doc.company }}" }
  ```

## 4. Missio Purchase Order Cancel Sync (NEW)

- **DocType:** Purchase Order
- **Doc Event:** on_cancel
- **Request URL:** `https://dev.missio.io/webhook/frappe/purchase-order`
- **JSON Request Body:**
  ```json
  {
    "name": "{{ doc.name }}",
    "company": "{{ doc.company }}",
    "docstatus": {{ doc.docstatus if doc.docstatus is defined else 0 }}
  }
  ```

## 5. Missio Purchase Invoice Cancel Sync (NEW)

- **DocType:** Purchase Invoice
- **Doc Event:** on_cancel
- **Request URL:** `https://dev.missio.io/webhook/frappe/purchase-invoice`
- **JSON Request Body:**
  ```json
  {
    "name": "{{ doc.name }}",
    "company": "{{ doc.company }}",
    "docstatus": {{ doc.docstatus if doc.docstatus is defined else 0 }}
  }
  ```

## 6. Missio Supplier Update Sync (NEW)

- **DocType:** Supplier
- **Doc Event:** on_update
- **Request URL:** `https://dev.missio.io/webhook/frappe/supplier`
- **JSON Request Body:**
  ```json
  {
    "name": "{{ doc.name }}",
    "supplier_name": "{{ doc.supplier_name }}",
    "disabled": {{ doc.disabled or 0 }}
  }
  ```
  (No `company` field — suppliers are company-independent in Frappe; Missio's handler looks up by `frappe_name` which is globally unique.)

## Troubleshooting

- **Missio returns 401 "invalid_signature"** — make sure **Enable Security** is ON and the Webhook Secret in Frappe matches the one shown on `/account/frappe-settings`. Regenerate it on the Missio side and repaste into every Frappe webhook.
- **Missio returns 404 "unknown_company"** — the `company` field in the JSON body must exactly match `company_frappe_settings.frappe_company_name`. For the Supplier webhook this doesn't apply (lookup is by `frappe_name`).
- **Webhook fires but nothing logs in Missio** — check `storage/logs/frappe-sync-YYYY-MM-DD.log` (the `frappe-sync` daily channel), not `laravel.log`.
- **The `company` field is `null` in Frappe's webhook log** — you forgot the JSON Request Body. Every webhook needs an explicit body template even with Enable Security on.
