CBA side illustration

CBA Business Banking Supplier and Invoice Register

Holds the approved supplier list and the invoice book for a business banking customer, and refuses any payment instruction naming a counterparty, an account or an amount that does not match what is on file.

This service is an illustration built on the CBA side of the boundary. It is not a Raidiam product. It exists to show what a resource server can demand of an agent, and to show that a refusal can always be explained.

What an agent has to discover

Resourcehttps://rs-supplier.demo.cba.raidiam.io
Authorization serverhttps://commbiz.demo.cba.raidiam.io
Trust anchornot configured
Detail typessupplier_invoice
Scopesopenid, suppliers.read, invoices.settle
Sender constrainingDPoP verified when presented, required for bound tokens
Mutabilityaccepts instructions

Authorization detail types

supplier_invoice

Authority to read the approved supplier list and the invoice book, and to settle an approved invoice. Settlement still needs a separate payment_initiation token addressed to the payments resource, because this resource never forwards the token it was called with.

Required members: type purpose

Optional members: invoice supplier instructedAmount constraints

{
  "type": "supplier_invoice",
  "purpose": "Settle approved supplier invoices for the August accounts payable run",
  "invoice": "INV-1041",
  "supplier": "SUP-2201",
  "instructedAmount": {
    "amount": "8200.00",
    "currency": "AUD"
  }
}

Token claims this resource decides on

Tools

ToolPurposeRequiresEffect
list_suppliers List the supplier records, with the approval status of each one. supplier_invoice read only
get_supplier Read one supplier record, including its approval status and the account recorded against it. supplier_invoice read only
list_invoices List invoices, optionally filtered by supplier or status. supplier_invoice read only
get_invoice Read one invoice, with the supplier it came from and the approval status of both. supplier_invoice read only
validate_payment_instruction Check a payment instruction against the approved supplier list, the invoice record and the account on file, without settling anything. supplier_invoice read only
settle_invoice Settle an approved invoice by instructing the payments resource, after the counterparty guardrails have passed. supplier_invoice changes state

Guardrails, published in advance

Every call carries a token from the named authorization server

Calls are accepted only with an access token issued by https://commbiz.demo.cba.raidiam.io and addressed to this resource as its audience. A token minted for a different resource is refused even when it is otherwise valid.

Refusal reason missing_access_token, invalid_token · decided at authorization · policy id rs.authenticated_caller

What clears it: Read this metadata document, then request a token from the authorization server it names, with this resource as the audience.

Authority is the RFC 9396 detail type, not a scope

Each tool names one authorization_details type. The token must carry that type, or an umbrella type that narrows to it. Holding a scope, or holding authority for a neighbouring resource, does not admit the call.

Refusal reason insufficient_authority · decided at authorization · policy id rs.authority_gate

What clears it: Obtain a token carrying the detail type the tool names. Delegation only ever narrows, so the delegating envelope must already contain it.

A revoked delegation stops working before its tokens expire

Revocation arrives as a Shared Signals event and is applied to the delegation, not to a single token. Every token issued under a revoked delegation is refused from that moment, whatever its expiry says.

Refusal reason delegation_revoked · decided at authorization · policy id rs.revocation_honoured

What clears it: The customer must grant a fresh delegation. There is no way to appeal a revocation at the resource.

Sender constrained tokens are bound to the key that holds them

A DPoP proof is verified whenever one is presented, and is required whenever the access token names a key in its cnf.jkt claim. Each proof is accepted once, so a captured proof cannot be replayed.

Refusal reason dpop_proof_required, invalid_dpop_proof, dpop_key_mismatch, dpop_proof_replayed, access_token_not_dpop_bound · decided at authorization · policy id rs.dpop_binding

What clears it: Request the access token with a DPoP proof so the authorization server binds it to your key, then send a fresh proof with every call.

A malformed request is refused with the reason it was malformed

Arguments are validated before any business rule runs, and the refusal names the argument at fault rather than returning a bare failure.

Refusal reason tool_error, invalid_amount, unknown_tool · decided at execution · policy id rs.request_validity

What clears it: Correct the named argument. The tool schemas are published in this document.

Money moves only to a counterparty on the approved supplier list

A supplier joins the approved list through a human onboarding process that confirms its bank details by callback to a number already on file. An agent may read the list. An agent may not add to it, and no payment instruction naming a counterparty outside it is passed on, whatever authority the agent holds and however plausible the invoice looks.

Refusal reason supplier_not_approved · decided at execution · policy id sup.approved_list

What clears it: None available to an agent. Onboarding a supplier is a human process.

The counterparty paid is the counterparty that issued the invoice

When an instruction names the supplier it believes it is paying, that name is checked against the invoice record. An agent that has been fed a substituted counterparty is refused on the mismatch rather than on the amount.

Refusal reason counterparty_mismatch · decided at execution · policy id sup.counterparty_matches_invoice

What clears it: Pay the supplier that issued the invoice, or name the invoice the other supplier issued.

The destination account is the one confirmed by callback

Matching the supplier name is not sufficient. The BSB and account number on the instruction are checked against the details recorded against that supplier. Substituting the account while keeping the name correct is the harder version of the attack, and it is refused here.

Refusal reason payee_account_mismatch · decided at execution · policy id sup.payee_account_on_file

What clears it: None available to an agent. Bank details change only through onboarding, with a callback to a number already on file.

Every payment settles a real invoice

An instruction that names no invoice in the book is refused. Accounts payable settles invoices, so a payment with nothing behind it has nothing to settle.

Refusal reason unknown_invoice · decided at execution · policy id sup.invoice_must_exist

What clears it: Read the invoice book and name an invoice from it.

Only an invoice the business has approved is settled

An invoice awaiting approval or in dispute is not settled by an agent, even when the supplier is approved and the amount is within every limit.

Refusal reason invoice_not_approved_for_payment · decided at execution · policy id sup.invoice_approved_for_payment

What clears it: The business approves the invoice first. That is a human step.

The amount paid is the amount owed

An accounts payable agent settles an invoice, it does not decide what the invoice is worth. An instruction for an amount other than the invoice total is refused.

Refusal reason invoice_amount_mismatch · decided at execution · policy id sup.amount_matches_invoice

What clears it: Instruct the invoice total, or raise the difference with the supplier as a credit note.

An invoice is settled once

Sub agents run in parallel and can be handed overlapping work. An invoice already settled is refused a second time here as well as at the payments resource, so a duplicate is caught before it reaches the payment rail.

Refusal reason invoice_already_settled · decided at execution · policy id sup.settle_once

What clears it: None. The invoice is paid.

Settlement carries its own audience scoped token

Settling an invoice means calling the payments resource, and this server never forwards the token it was called with. The caller supplies a separate payment token addressed to the payments resource, and the payments resource applies its own envelope and single payment limit on top of everything decided here.

Refusal reason payment_token_required · decided at execution · policy id sup.separate_payment_token

What clears it: Obtain a payment_initiation token addressed to the payments resource and pass it as payment_token.

Observability

Every admission decision, allowed and refused, is recorded with the policy that decided it and the values it turned on. Read them at /decisions.