ProcureOps AP Release & Change-Control Skill
Overview
This skill covers tasks that interact with the ProcureOps API to produce structured JSON decisions for procurement workflows: AP release/hold files, receiving batch reviews, AP close reconciliations, change-control decisions, and nomination readiness packets.
API Endpoints
The ProcureOps API exposes these endpoints (all return {count, results} list envelopes unless noted):
/programs— program metadata includingbudget_cap,committed_amount,owner,status/purchase_orders— PO records withlines[{quantity, unit_price, sku}],status,contract_id,program_id,total,subtotal/purchase_requisitions— requisition records withstatus(draft/submitted/approved/converted/cancelled),sku,program_id/receipts— receipt records withlines[{quantity_received, quantity_rejected, inspection_status}],status,po_id,receipt_date/ap_invoices— invoice records withlines[{quantity_billed, unit_price}],status(on_hold/approved/pending_receipt/paid),hold_code,receipt_id,total,subtotal,freight,tax/payments— payment records withinvoice_id,amount,status(scheduled/released/blocked),scheduled_date/contracts— contract records withsku,unit_price,ceiling_amount,status,program_id,supplier_id/budget_snapshots— budget records withbudget_cap,committed_amount,pending_invoice_amount,program_id,snapshot_id/suppliers— supplier records withrisk_rating(low/medium/watch/high),status,name/vendor_risk_events— risk events withsupplier_id,severity(low/medium/high/critical),status(open/closed/monitoring),event_id/approval_events— approval workflow events withobject_id,object_type,action,actor,event_date,event_id
Universal Conventions
- Task ID: The JSON output
task_idmust be exactlytrain_001throughtrain_005(or the equivalent test task ID). Do not use descriptive strings. - Sorting: Unless a template specifies otherwise, sort all ID lists ascending alphabetically. This includes
po_ids,receipt_ids,invoice_ids,supplier_ids,event_ids. - Temporal scope: When a task specifies
as_of_dateorreview_as_of, only count receipts/invoices/events whose dates are on or before that date. Receipts dated after the as-of date do not exist for that review. - Rounding: Currency amounts to 2 decimals. Percentages to the precision specified in the template (commonly 1 decimal for percentages, 4 for ratios).
- Opening balance rule: For AP close-slice reconciliations that say "Treat the opening balance for target suppliers as 0.00 USD", use exactly
0.0. - Program scope: When a task references a specific program (e.g.,
PRG-AX17), always query all records in that program even if the memo only highlights certain POs. Related POs for the same SKU in the same program must be included where the template asks for package or included POs.
Task-Type Patterns
1. Nomination Readiness (train_001 style)
Goal: Determine whether SKU lines can proceed to committee nomination.
Key logic:
- Identify all POs for each target SKU in the program, not just the PO named in the memo.
commercial_basis_idis the contract ID if a contract exists for that SKU/program; otherwisenull.receipt_evidence_ids: receipts for those POs withreceipt_date <= as_of_date.invoice_exception_ids: invoices for those POs that areon_holdorpending_receiptas of the review date.risk_event_ids: open or monitoringvendor_risk_eventsfor the supplier as of the review date. Do not assume empty just because the supplier rating iswatch; always check the risk event endpoint.blocker_codes(sorted ascending): includemissing_contractwhen no contract exists;pending_receiptwhen no receipt exists as of the review date;ap_holdwhen an invoice exception exists;open_supplier_riskwhen an open risk event exists;supplier_watchwhen the supplierrisk_ratingiswatch.readiness_status:readyonly if no blockers;at_riskif blockers exist but are not fatal;not_readyif fatal blockers exist (e.g., no contract for a required commercial basis, or critical open risk).nomination_decision:nominateforready,conditional_nominationforat_risk,holdfornot_ready.overall_readiness: reflects the worst line status (if any line isnot_ready→not_ready; if any isat_risk→at_risk; elseready).committee_action: routenominate_nowforreadysuppliers,conditionalforat_risk,holdfornot_ready.send_to_committeeis typicallyyesunless nothing is ready or conditional.
2. Receiving Batch Review (train_002 style)
Goal: Evaluate a single receipt batch against its PO, invoice, contract, and supplier risk.
Key logic:
line_reconciliation: computeshort_qty_vs_po = ordered_qty - received_qty,unreceived_billed_qty = billed_qty - received_qty,receipt_completion_ratio = round(received_qty / ordered_qty, 4).contract_price_match:truewhen the PO unit price equals the contract unit price.exception_codes(set, evaluator sorts): includeINVOICE_QTY_EXCEEDS_RECEIPTwhenbilled_qty > received_qty;PARTIAL_RECEIPTwhenreceived_qty < ordered_qty;SUPPLIER_WATCH_RISKwhen the supplierrisk_ratingiswatchand there is an open risk event (or simply when the rating iswatch— include it);PRICE_MISMATCHwhen unit prices differ;DAMAGE_REJECTIONwhenquantity_rejected > 0.batch_disposition:accept_partial_hold_variancewhen the receipt is accepted but quantities differ;release_full_invoiceonly when everything matches perfectly;manual_recount_requiredfor severe discrepancies.ap_action:keep_invoice_on_holdwhen the invoice has a hold code or variance;release_invoicewhen matched and no holds.receiving_action:record_shortage_follow_upwhen there is a short quantity;no_receiving_actionwhen the receipt is already posted and no further receiving step is needed. The memo text ("closeout review for an already-posted receipt") suggestsrecord_shortage_follow_upis appropriate when a shortage exists, because the follow-up still needs to be documented.supplier_action:request_credit_or_remaining_deliverywhen there is a shortage or variance;no_supplier_actionwhen everything is clean.supplier_risk_context: always query open risk events for the supplier. Includehas_open_supplier_risk: trueand the open event IDs when any exist.
3. AP Close Reconciliation (train_003 style)
Goal: Reconcile target invoices, compute vendor balances, program summaries, and payment queues.
Key logic:
invoice_decisions: one row per target invoice, sorted byinvoice_idascending.quantity_received: from the linked receipt if any;0.00if no receipt exists.quantity_variance = round(quantity_billed - quantity_received, 2).quantity_variance_pct = round((variance / PO_quantity) * 100, 1).hold_decision:HOLDfor invoices withstatusin (on_hold,pending_receipt);RELEASEotherwise.release_to_payment:truefor approved/matched invoices, even if a scheduled payment already exists. The scheduled payment is a separate fact.reason_codes(sorted alphabetically):APPROVED_THREE_WAY_MATCHwhen status isapprovedand quantities match.NO_RECEIPTwhen no receipt exists.QTY_VARIANCEwhenquantity_billed != quantity_received.SCHEDULED_PAYMENT_FOUNDwhen a payment record exists for this invoice.
vendor_balances(sorted bysupplier_idascending):opening_balance = 0.0per the memo.invoice_total= sum of target invoice totals for that supplier.scheduled_payments= sum of payment amounts for target invoices of that supplier.held_invoice_total= sum of target invoice totals withstatusin (on_hold,pending_receipt).releasable_invoice_total= sum of target invoice totals that are approved/releasable. For a supplier whose invoices are all approved and fully scheduled, this can be the approved total (not zero); thebalance_statusdistinguishes the state.close_balance = round(opening_balance + invoice_total - scheduled_payments, 2).balance_status:OPEN_HELDwhenheld_invoice_total > 0.FULLY_SCHEDULEDwhenheld_invoice_total == 0and all approved amounts are covered by scheduled payments.OPEN_APPROVEDwhen there are approved invoices not fully scheduled.
program_summary(sorted byprogram_idascending): aggregate the target invoices by program.net_close_balanceis the sum ofclose_balancefor suppliers in that program.payment_hold_queue: sorted target invoice IDs withhold_decision == HOLD.payment_release_queue: sorted target invoice IDs withhold_decision == RELEASE.total_close_balance: sum of all supplierclose_balancevalues.
4. Change-Control Decision (train_004 style)
Goal: Determine whether a modular change can be released as a contract amendment.
Key logic:
contract_check:noncancelled_subtotal= sum ofsubtotalfor all POs linked to the contract withstatus != cancelled.headroom_before_change = ceiling_amount - noncancelled_subtotal.requested_subtotal = requested_quantity * unit_price.headroom_after_change = headroom_before_change - requested_subtotal.ceiling_ok:truewhenheadroom_after_change >= 0.
program_budget_check:- Use the
budget_snapshotsrecord for the program. remaining_budget = budget_cap - committed_amount.requested_tax = round(requested_subtotal * tax_rate_percent / 100, 2).requested_total = round(requested_subtotal + requested_tax, 2).budget_after_change = round(remaining_budget - requested_total, 2).budget_ok:truewhenbudget_after_change >= 0.max_quantity_with_current_budget = floor(remaining_budget / (unit_price * (1 + tax_rate))).
- Use the
approval_check:- Find all
approval_eventswhereobject_id == source_requisition_idandobject_type == requisition. - Use the latest event by
event_date. approval_ok:truewhenlatest_actionis in the good-actions list (typically["approved"]).
- Find all
supplier_risk_check:open_event_ids: all open/monitoring risk events for the supplier, sorted ascending.severe_open_event_ids: subset with severity in (high,critical), sorted ascending.supplier_risk_ok:truewhensevere_open_event_idsis empty.
supporting_ids:included_po_ids: all non-cancelled POs under the contract, sorted ascending.excluded_cancelled_po_ids: cancelled POs under the contract, sorted ascending.approval_event_ids: all approval events for the source requisition, sorted ascending.
required_actions(sorted ascending):obtain_final_requisition_approvalwhenapproval_ok == false.raise_budget_exception_or_reduce_quantitywhenbudget_ok == falseorceiling_ok == false.resolve_supplier_risk_holdwhensupplier_risk_ok == false.nonewhen no blockers.
decision:release_amendmentwhen all checks pass.hold_for_budgetwhen only budget fails.hold_for_approvalwhen only approval fails.hold_for_supplier_riskwhen only supplier risk fails.hold_for_budget_and_approvalwhen both budget and approval fail.reject_contract_mismatchwhen the contract is inactive or the SKU does not match.
5. AP Release with Chargebacks (train_005 style)
Goal: Build release/hold decisions and receiving exceptions for invoices tied to a program, using both API data and a local chargeback register.
Key logic:
release_decisions: one entry per target invoice, sorted byinvoice_idascending.receipt_ids_in_scope: receipts explicitly linked to the invoice or PO.excluded_same_po_receipt_ids: other receipts on the same PO that are not in scope for this invoice. Include receipts from the full API, not just the target receipt list in the packet, when they belong to a target PO.decision:release_net_after_approved_chargebackwhen anapprovedchargeback exists for the invoice/receipt.hold_missing_receiptwhen the invoice has no receipt andhold_codeisNO_RECEIPT.hold_pending_quality_chargebackwhen a chargeback ispending_quality_reviewand/or the receipt is oninspection_hold.
primary_reason:approved_qty_chargebackfor approved underage-quantity chargebacks.approved_ap_quantity_variancefor approved AP-quantity-variance chargebacks.no_receipt_on_pofor missing receipts.inspection_hold_pending_chargebackfor pending quality chargebacks.
approved_chargeback_amount=basis_quantity * unit_costfor approved chargebacks;0.0otherwise.pending_chargeback_amount=basis_quantity * unit_costfor pending chargebacks;0.0otherwise.net_release_amount=invoice_total - approved_chargeback_amountfor released invoices;0.0for held invoices.
receiving_exceptions: one entry per target receipt, sorted byreceipt_idascending.exception_codes(set, evaluator sorts):Underage Quantitywhenquantity_received < PO_quantity.AP Quantity Variancewhenquantity_billed > quantity_received.Inspection Holdwhen receiptstatus == inspection_hold.Severe Unmatched Quantitywhen the billed-to-received or PO-to-received discrepancy is very large (use judgment; a billed quantity vastly exceeding received or PO quantity qualifies).
chargeback_status:approved,pending_quality_review, ornot_applicable.resolution_status:net_release_readywhen chargeback is approved;hold_for_quality_reviewwhen pending;accepted_no_receiving_exceptionwhen no exception;missing_receiptwhen no receipt exists.
summary:release_invoice_ids: target invoices withrelease_*decisions.hold_invoice_ids: target invoices withhold_*decisions.approved_chargeback_total: sum of approved chargeback amounts.pending_chargeback_total: sum of pending chargeback amounts.net_release_total: sum ofnet_release_amountacross all invoices (held ones contribute0.0).authoritative_sources:[procureops_po_records, procureops_receipt_records, procureops_ap_records, local_chargeback_register].supporting_only_sources:[ap_release_request_note, stale_po73xx_alias_note].followup_actions: choose from the allowed list based on the issues found. Common selections:ask_receiving_for_vantix_receiptwhen a Vantix invoice lacks a receipt.hold_luma_duplicate_receipt_for_separate_invoicewhen a PO has multiple receipts for different invoices.route_po00031_quality_reviewwhen a receipt is on inspection hold with a pending chargeback.post_approved_chargeback_nettingwhen approved chargebacks exist.
Common Pitfalls
- Forgetting the as-of date filter: Receipts or invoices with dates after the review date must be excluded from evidence counts.
- Omitting related POs: A memo may highlight one PO, but the same SKU often has multiple POs in the program. Include all non-cancelled POs for contract and budget calculations.
- Risk event confusion: A supplier with
risk_rating: watchmay still have open risk events. Always query/vendor_risk_eventsand include open events inrisk_event_idsand blocker codes. - Chargeback status: Distinguish
approvedvspending_quality_reviewchargebacks. Onlyapprovedones reduce the net release amount. - Releasable invoice total: In vendor balances, approved invoices are releasable even if fully scheduled. Do not zero them out unless the template explicitly says otherwise.
- Tax calculation: For change-control tasks, tax is
round(subtotal * tax_rate_percent / 100, 2). Freight is only included if the memo explicitly provides a freight line. - Approval events: A requisition may have multiple approval events. Use the latest by event_date, not just any event.
- Max quantity with budget: Use
floor(remaining_budget / (unit_price * (1 + tax_rate))), not a simple division without the tax factor. - Sorting: Unsorted ID lists are a frequent source of score loss. Sort every list the template describes as ordered.
- Exception code scope:
Severe Unmatched Quantityis reserved for very large discrepancies (e.g., billed quantity vastly exceeds received quantity). Do not apply it to small variances like 10%.