Enforcement points
An enforcement point is the system that releases money: a payment hub, an ERP payment run, an execution platform. Polivor does not control it. The authorization is what lets it prove the payment was approved, and refuse anything that was not.
What an authorization is
A compact JWS (three dot-separated parts) with header typ: polivor-authz+jwt, alg: EdDSA and a kid. The payload:
{
"iss": "https://api.staging.polivor.com",
"aud": "<your enforcement point audience>",
"sub": "agent:<agent id>@3.7",
"jti": "<authorization id>",
"iat": 1794650400,
"exp": 1794650700,
"tenant": "<tenant id>",
"principal": "<principal id>",
"decision_id": "<decision id>",
"mandate": { "id": "<mandate id>", "version": 1 },
"policy_version": 1,
"action": {
"type": "PayInvoice",
"amount_minor": 420000,
"currency": "EUR",
"debtor_account": "ES9121000418450200051332",
"payment_type": "SEPA_CT",
"beneficiary_iban": "ES7620770024003102575766",
"reference": "INV-104"
},
"action_sha256": "<hex>"
}
The aud is your enforcement point audience, chosen when the tenant is created. The exp is 5 minutes after iat.
Verify it before you release funds
You need only the public keys: GET /.well-known/jwks.json (no key needed). Cache them, and refetch when you see an unknown kid. Keys with use: authz sign authorizations; use: evidence keys sign evidence and must not be accepted here.
Refuse the payment unless all of these hold:
- The signature is valid for the
kidin the header, with a key whoseuseisauthzand that is not revoked. - The header
typispolivor-authz+jwtandalgisEdDSA. Reject any other algorithm. - The current time is before
exp. audequals your enforcement point audience.- The payment you are about to make matches
actionexactly:amount_minor,currency,debtor_account,payment_type,beneficiary_iban(normalised: no spaces, upper case) andreference. A different amount, account or reference means a different payment. action_sha256equals the hex SHA-256 of the JSON Canonicalization Scheme (RFC 8785) form of theactionobject. This stops anyone swapping fields while keeping a valid signature on another part.- The
jtihas not been used before. Remember usedjtivalues until the authorization has expired, and refuse a second use.
Complete, tested code for these checks in Java and Go is in Verify in Java, Go and .NET. If any check fails, do not pay, and report REJECTED (see below) if you accepted the instruction earlier. Polivor itself ships a reference enforcement point in the console (the payment hub), which refuses a tampered amount, an expired authorization, a replay and a wrong IBAN, so you can compare behaviours.
Report the result
After you act, report with the enforcer key. Exactly one terminal report is accepted per authorization.
{ "status": "REJECTED", "external_ref": "hub-refusal-0042" }
| Status | Meaning | Budget |
|---|---|---|
EXECUTED | The payment was released. | Stays used. |
REJECTED | You refused it (verification failed, or the bank refused). | Given back. |
FAILED | You tried and it failed. | Given back. |
A second report returns 409 ALREADY_REPORTED. A report more than 24 hours after the authorization expired returns 410 REPORT_WINDOW_CLOSED. An authorization nobody reports on is shown in the console; an admin can release it with POST /v1/authorizations/{id}/not-executed once it has expired, which gives the budget back and writes evidence.
Unreported authorizations
An authorization that was issued but never reported is a signal that your integration has a gap. Polivor counts them and alerts its operators when many are older than an hour. Report every outcome, including refusals.