Procure to pay BPMN example
Procure to pay (P2P) runs from the moment someone in the business needs to buy something to the moment the supplier is paid. Teams map it because spend control depends on it: an unapproved requisition, a missing goods receipt, or an unmatched invoice each leaks money in a different way. The usual failure point is the gap between ordering and paying, where paperwork from three sources has to agree.
This model uses a single pool with four lanes: Requester, Procurement, Warehouse, and Accounts payable. Two exclusive gateways carry the decisions, one for requisition approval and one for the three way match, and the failed match branch loops back through a discrepancy task instead of ending the process. That loop is the modelling choice worth noticing: mismatches are normal work, not terminal errors.
Standard BPMN 2.0 interchange XML: opens in Camunda Modeler, Signavio, Bizagi, and any other compliant tool.
The process at a glance
How to read this diagram
Everything begins in the Requester lane when a purchase need is identified and a requisition is raised. Procurement reviews it, and the exclusive gateway "Requisition approved?" splits the flow: the No branch, marked as the default, notifies the requester and finishes at the "Requisition rejected" end event, while the Yes branch continues to purchase order creation. A send task passes the order to the supplier, and the Warehouse lane records the goods receipt when the delivery lands.
Accounts payable then runs the three way match, checking the supplier invoice against the purchase order and the goods receipt. At the second gateway, "Match successful?", a failed match takes the default No branch into "Resolve invoice discrepancy" and loops straight back to the match task until the figures agree. Once the Yes branch is taken, the payment is executed and the process closes at "Supplier paid".
BPMN elements used
| Element | Count | In this diagram |
|---|---|---|
| Start event | 1 | Purchase need identified |
| End event | 2 | Requisition rejected, Supplier paid |
| Task | 1 | Record goods receipt |
| User task | 3 | Raise purchase requisition, Review requisition, Resolve invoice discrepancy |
| Service task | 2 | Create purchase order, Execute supplier payment |
| Send task | 2 | Notify requester of rejection, Send PO to supplier |
| Business rule task | 1 | Run three way match |
| Exclusive gateway | 2 | Requisition approved?, Match successful? |
View the BPMN 2.0 XML for this diagram
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="swimdraft" exporterVersion="1.0">
<bpmn2:collaboration id="Collaboration_1">
<bpmn2:participant id="pool_p2p" name="Procure to pay" processRef="Process_pool_p2p" />
</bpmn2:collaboration>
<bpmn2:process id="Process_pool_p2p" isExecutable="false">
<bpmn2:laneSet id="LaneSet_pool_p2p">
<bpmn2:lane id="lane_p2p_req" name="Requester">
<bpmn2:flowNodeRef>start_p2p</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_p2p_raise</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_p2p_proc" name="Procurement">
<bpmn2:flowNodeRef>t_p2p_review</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>g_p2p_approve</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_p2p_notify</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>end_p2p_rejected</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_p2p_po</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_p2p_send</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_p2p_wh" name="Warehouse">
<bpmn2:flowNodeRef>t_p2p_receive</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_p2p_ap" name="Accounts payable">
<bpmn2:flowNodeRef>t_p2p_match</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>g_p2p_match</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_p2p_resolve</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_p2p_pay</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>end_p2p_paid</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<bpmn2:startEvent id="start_p2p" name="Purchase need identified">
<bpmn2:outgoing>fp2p1</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:userTask id="t_p2p_raise" name="Raise purchase requisition">
<bpmn2:incoming>fp2p1</bpmn2:incoming>
<bpmn2:outgoing>fp2p2</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:userTask id="t_p2p_review" name="Review requisition">
<bpmn2:incoming>fp2p2</bpmn2:incoming>
<bpmn2:outgoing>fp2p3</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:exclusiveGateway id="g_p2p_approve" name="Requisition approved?" default="fp2p5">
<bpmn2:incoming>fp2p3</bpmn2:incoming>
<bpmn2:outgoing>fp2p4</bpmn2:outgoing>
<bpmn2:outgoing>fp2p5</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sendTask id="t_p2p_notify" name="Notify requester of rejection">
<bpmn2:incoming>fp2p5</bpmn2:incoming>
<bpmn2:outgoing>fp2p6</bpmn2:outgoing>
</bpmn2:sendTask>
<bpmn2:endEvent id="end_p2p_rejected" name="Requisition rejected">
<bpmn2:incoming>fp2p6</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:serviceTask id="t_p2p_po" name="Create purchase order">
<bpmn2:incoming>fp2p4</bpmn2:incoming>
<bpmn2:outgoing>fp2p7</bpmn2:outgoing>
</bpmn2:serviceTask>
<bpmn2:sendTask id="t_p2p_send" name="Send PO to supplier">
<bpmn2:incoming>fp2p7</bpmn2:incoming>
<bpmn2:outgoing>fp2p8</bpmn2:outgoing>
</bpmn2:sendTask>
<bpmn2:task id="t_p2p_receive" name="Record goods receipt">
<bpmn2:incoming>fp2p8</bpmn2:incoming>
<bpmn2:outgoing>fp2p9</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:businessRuleTask id="t_p2p_match" name="Run three way match">
<bpmn2:incoming>fp2p9</bpmn2:incoming>
<bpmn2:incoming>fp2p13</bpmn2:incoming>
<bpmn2:outgoing>fp2p10</bpmn2:outgoing>
</bpmn2:businessRuleTask>
<bpmn2:exclusiveGateway id="g_p2p_match" name="Match successful?" default="fp2p12">
<bpmn2:incoming>fp2p10</bpmn2:incoming>
<bpmn2:outgoing>fp2p11</bpmn2:outgoing>
<bpmn2:outgoing>fp2p12</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:userTask id="t_p2p_resolve" name="Resolve invoice discrepancy">
<bpmn2:incoming>fp2p12</bpmn2:incoming>
<bpmn2:outgoing>fp2p13</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:serviceTask id="t_p2p_pay" name="Execute supplier payment">
<bpmn2:incoming>fp2p11</bpmn2:incoming>
<bpmn2:outgoing>fp2p14</bpmn2:outgoing>
</bpmn2:serviceTask>
<bpmn2:endEvent id="end_p2p_paid" name="Supplier paid">
<bpmn2:incoming>fp2p14</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="fp2p1" sourceRef="start_p2p" targetRef="t_p2p_raise" />
<bpmn2:sequenceFlow id="fp2p2" sourceRef="t_p2p_raise" targetRef="t_p2p_review" />
<bpmn2:sequenceFlow id="fp2p3" sourceRef="t_p2p_review" targetRef="g_p2p_approve" />
<bpmn2:sequenceFlow id="fp2p4" name="Yes" sourceRef="g_p2p_approve" targetRef="t_p2p_po">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">Yes</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="fp2p5" name="No" sourceRef="g_p2p_approve" targetRef="t_p2p_notify" />
<bpmn2:sequenceFlow id="fp2p6" sourceRef="t_p2p_notify" targetRef="end_p2p_rejected" />
<bpmn2:sequenceFlow id="fp2p7" sourceRef="t_p2p_po" targetRef="t_p2p_send" />
<bpmn2:sequenceFlow id="fp2p8" sourceRef="t_p2p_send" targetRef="t_p2p_receive" />
<bpmn2:sequenceFlow id="fp2p9" sourceRef="t_p2p_receive" targetRef="t_p2p_match" />
<bpmn2:sequenceFlow id="fp2p10" sourceRef="t_p2p_match" targetRef="g_p2p_match" />
<bpmn2:sequenceFlow id="fp2p11" name="Yes" sourceRef="g_p2p_match" targetRef="t_p2p_pay">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">Yes</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="fp2p12" name="No" sourceRef="g_p2p_match" targetRef="t_p2p_resolve" />
<bpmn2:sequenceFlow id="fp2p13" sourceRef="t_p2p_resolve" targetRef="t_p2p_match" />
<bpmn2:sequenceFlow id="fp2p14" sourceRef="t_p2p_pay" targetRef="end_p2p_paid" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1">
<bpmndi:BPMNShape id="pool_p2p_di" bpmnElement="pool_p2p" isHorizontal="true">
<dc:Bounds x="20" y="20" width="1720" height="740" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_p2p_req_di" bpmnElement="lane_p2p_req" isHorizontal="true">
<dc:Bounds x="50" y="20" width="1690" height="130" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_p2p_proc_di" bpmnElement="lane_p2p_proc" isHorizontal="true">
<dc:Bounds x="50" y="150" width="1690" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_p2p_wh_di" bpmnElement="lane_p2p_wh" isHorizontal="true">
<dc:Bounds x="50" y="390" width="1690" height="130" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_p2p_ap_di" bpmnElement="lane_p2p_ap" isHorizontal="true">
<dc:Bounds x="50" y="520" width="1690" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="start_p2p_di" bpmnElement="start_p2p">
<dc:Bounds x="112" y="59" width="36" height="36" />
<bpmndi:BPMNLabel><dc:Bounds x="68" y="101" width="124" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_raise_di" bpmnElement="t_p2p_raise">
<dc:Bounds x="230" y="37" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_review_di" bpmnElement="t_p2p_review">
<dc:Bounds x="380" y="167" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="g_p2p_approve_di" bpmnElement="g_p2p_approve">
<dc:Bounds x="555" y="182" width="50" height="50" />
<bpmndi:BPMNLabel><dc:Bounds x="524" y="162" width="112" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_notify_di" bpmnElement="t_p2p_notify">
<dc:Bounds x="680" y="277" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="end_p2p_rejected_di" bpmnElement="end_p2p_rejected">
<dc:Bounds x="862" y="299" width="36" height="36" />
<bpmndi:BPMNLabel><dc:Bounds x="831" y="341" width="99" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_po_di" bpmnElement="t_p2p_po">
<dc:Bounds x="680" y="167" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_send_di" bpmnElement="t_p2p_send">
<dc:Bounds x="830" y="167" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_receive_di" bpmnElement="t_p2p_receive">
<dc:Bounds x="980" y="407" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_match_di" bpmnElement="t_p2p_match">
<dc:Bounds x="1130" y="537" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="g_p2p_match_di" bpmnElement="g_p2p_match">
<dc:Bounds x="1305" y="552" width="50" height="50" />
<bpmndi:BPMNLabel><dc:Bounds x="1284" y="532" width="93" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_resolve_di" bpmnElement="t_p2p_resolve">
<dc:Bounds x="1430" y="647" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_p2p_pay_di" bpmnElement="t_p2p_pay">
<dc:Bounds x="1430" y="537" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="end_p2p_paid_di" bpmnElement="end_p2p_paid">
<dc:Bounds x="1612" y="559" width="36" height="36" />
<bpmndi:BPMNLabel><dc:Bounds x="1597" y="601" width="67" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="fp2p1_di" bpmnElement="fp2p1">
<di:waypoint x="148" y="77" />
<di:waypoint x="230" y="77" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p2_di" bpmnElement="fp2p2">
<di:waypoint x="330" y="77" />
<di:waypoint x="360" y="77" />
<di:waypoint x="360" y="207" />
<di:waypoint x="380" y="207" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p3_di" bpmnElement="fp2p3">
<di:waypoint x="480" y="207" />
<di:waypoint x="555" y="207" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p4_di" bpmnElement="fp2p4">
<di:waypoint x="605" y="207" />
<di:waypoint x="680" y="207" />
<bpmndi:BPMNLabel><dc:Bounds x="611" y="187" width="21" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p5_di" bpmnElement="fp2p5">
<di:waypoint x="605" y="207" />
<di:waypoint x="660" y="207" />
<di:waypoint x="660" y="317" />
<di:waypoint x="680" y="317" />
<bpmndi:BPMNLabel><dc:Bounds x="611" y="213" width="17" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p6_di" bpmnElement="fp2p6">
<di:waypoint x="780" y="317" />
<di:waypoint x="862" y="317" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p7_di" bpmnElement="fp2p7">
<di:waypoint x="780" y="207" />
<di:waypoint x="830" y="207" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p8_di" bpmnElement="fp2p8">
<di:waypoint x="930" y="207" />
<di:waypoint x="960" y="207" />
<di:waypoint x="960" y="447" />
<di:waypoint x="980" y="447" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p9_di" bpmnElement="fp2p9">
<di:waypoint x="1080" y="447" />
<di:waypoint x="1110" y="447" />
<di:waypoint x="1110" y="577" />
<di:waypoint x="1130" y="577" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p10_di" bpmnElement="fp2p10">
<di:waypoint x="1230" y="577" />
<di:waypoint x="1305" y="577" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p11_di" bpmnElement="fp2p11">
<di:waypoint x="1355" y="577" />
<di:waypoint x="1430" y="577" />
<bpmndi:BPMNLabel><dc:Bounds x="1361" y="557" width="21" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p12_di" bpmnElement="fp2p12">
<di:waypoint x="1355" y="577" />
<di:waypoint x="1410" y="577" />
<di:waypoint x="1410" y="687" />
<di:waypoint x="1430" y="687" />
<bpmndi:BPMNLabel><dc:Bounds x="1361" y="583" width="17" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p13_di" bpmnElement="fp2p13">
<di:waypoint x="1530" y="687" />
<di:waypoint x="1560" y="687" />
<di:waypoint x="1560" y="742" />
<di:waypoint x="1110" y="742" />
<di:waypoint x="1110" y="577" />
<di:waypoint x="1130" y="577" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fp2p14_di" bpmnElement="fp2p14">
<di:waypoint x="1530" y="577" />
<di:waypoint x="1612" y="577" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>Frequently asked questions
Why is the supplier not modelled as a second pool?
The diagram deliberately stays inside one organisation, so the supplier only appears indirectly through the send task and the goods receipt. To model the conversation explicitly, add a second pool as a black box with no internal detail and connect it with message flows from "Send PO to supplier". Keeping it out keeps the diagram focused on internal handoffs.
Why does the failed match loop back instead of ending the process?
A mismatched invoice is routine work, not a dead end, so the No branch routes through "Resolve invoice discrepancy" and rejoins the match task. Ending the process there would strand real invoices that just need a correction. BPMN handles this kind of rework loop naturally, provided every node still sits on a path to an end event.
Why is the three way match a business rule task?
The match is a pure decision over data: compare the invoice, purchase order, and goods receipt, then report whether they agree within tolerance. BPMN reserves the business rule task type for exactly this, evaluation by a rules engine or decision table. If your organisation matches invoices by hand, change it to a user task and the flow still validates.
Related BPMN examples
Order to cash
A complete order to cash process as a BPMN 2.0 diagram: credit check, fulfilment, invoicing, and payment. View the swimlane diagram and download the .bpmn file free.
Invoice approval
Invoice approval as a BPMN 2.0 swimlane diagram: amount thresholds, manager and director approval, and posting to the ledger. View it and download the .bpmn file free.
Expense reimbursement
An expense reimbursement process in BPMN 2.0: policy checks, receipt audit, an amendment loop, and a timed payment run. View the diagram and download the .bpmn free.
Accounts payable
Accounts payable in BPMN 2.0 with a black box supplier pool: invoice matching, supplier queries by message flow, and a timed payment run. Free .bpmn download.
Generate your own version of this diagram
Describe how the process works in your organisation: plain English, meeting notes, a transcript, or a spreadsheet. Swimdraft turns it into a spec-validated BPMN 2.0 diagram you can edit in the browser and export as .bpmn, SVG, or PNG.
Start freeNo credit card required