Skip to content

Payments and treasury operations

Sign the beneficiary and the amount, not the session.

Payment fraud rarely defeats an approval workflow. It arrives through one — an authorized person approving a payment whose details changed after they looked, or a compromised service spending an entitlement granted months earlier for something else entirely. Both are failures of binding: the approval was attached to a person's session or role, not to the beneficiary and the figure that actually left the account.

The integration point

Call INTYGA immediately before submitting the payment, with the values you are about to submit. The approver sees those exact values on their own device and signs them.

  1. Construct the beneficiary, amount and currency your code will actually send.
  2. Request a human approval and wait for the signed result.
  3. Re-verify the receipt locally against those same values, then submit once.

If anything about the payment changes between the request and the submission, the receipt no longer verifies and the code refuses to send it. That check runs in your process, against parameters you derived, with no call back to us.

The approval is also single-use and bound to the executing system. Redeeming it a second time fails, and an approval granted for the payments service cannot be spent by the refunds service — the target is part of what was signed.

const action = {
  target: PAYMENTS_TARGET,   // this service — so the approval can't be replayed against another
  actionType: "wire_transfer",
  params: { beneficiaryIban, amountCents: 2500000, currency: "EUR" }
};

const approval = await intyga.requireApproval("Wire — 25,000.00 EUR", action);
if (approval.status !== "APPROVED") throw new Error("not approved");

// nonce and approvers come from YOUR side — a receipt cannot vouch for its own signer.
const anchor = parseTrustAnchorFile(readFileSync(process.env.INTYGA_APPROVERS_FILE!, "utf8"));
const TRUSTED_APPROVERS = trustAnchorApprovers(anchor);   // quorum counts PEOPLE, not keys
const check = verifyApprovalReceipt(approval.receipt!, {
  ...action, nonce: approval.nonce!, approvers: TRUSTED_APPROVERS,
}, {
  expectedOrigin: anchor.webauthn?.origin,
  expectedRpId: anchor.webauthn?.rpId,
});
if (!check.ok) throw new Error("receipt mismatch: " + check.reason);

await submitPayment(beneficiaryIban, 2500000);

What the approver actually sees

Not a summary the requesting service composed, and not an email that could have been spoofed. The notification carries an opaque reference and a kind — no beneficiary, no amount, no account details, no PII of any kind. The approver's browser fetches the authoritative payment from the gateway over TLS and displays every parameter key by key.

Those canonical bytes are what the passkey signs, and the same bytes are what your payment service recomputes before submitting. There is no gap between what was displayed, what was signed, and what executes — which is precisely the gap business-email-compromise fraud lives in.

It also means a leaked notification, a forwarded message, or a compromised chat channel discloses nothing about the payment itself.

{
  "target": "payments-prod",
  "actionType": "wire_transfer",
  "display": "Wire — 25,000.00 EUR",
  "params": {
    "beneficiaryIban": "DE89 3704 0044 0532 0130 00",
    "beneficiaryName": "Acme GmbH",
    "amountCents": 2500000,
    "currency": "EUR",
    "invoiceRef": "INV-2026-0431"
  },
  "nonce": "single-use challenge",
  "expiresAt": "short approval window"
}

What to gate first

Transfers above a threshold

Bind the beneficiary account, the amount, and the currency. A signature for 25,000 to one IBAN cannot be replayed against a different account or a different figure.

Beneficiary and payee changes

The bank-details update is the step the fraud actually targets, and it is usually gated more weakly than the payment it enables.

Refunds and adjustments issued by agents

A support agent or an automated workflow can request; a person approves. The record keeps the two apart rather than inferring it from a role.

Payout batches and payroll runs

Bind the file digest and the total. A batch that gained a line after review no longer matches what was signed.

Treasury movements between accounts

Sweeps, FX conversions and inter-company transfers are high-value, routine, and rarely re-checked at execution time.

Limit and mandate changes

Raising a spending ceiling or adding a signatory should be at least as hard to do as spending against it. Gate the control plane, not only the payment.

Two people, enforced where the requester cannot reach it

Quorum, four-eyes and hardware-key requirements are server-side approval rules, frozen onto the challenge at the moment it is created. A caller can request an authorization; it cannot ask for a lower threshold, and a compromised service cannot lower its own.

  • M-of-N quorum — distinct valid signatures from distinct approvers. Two treasury signatories for anything above a limit, expressed once and enforced everywhere.
  • Four-eyes — whoever raised the payment cannot be the one who ratifies it.
  • Hardware-key class — require a device-bound authenticator, optionally narrowed to your corporate security-key model. Synced passkeys are rejected.
  • Approver groups, snapshotted — eligibility is captured when the challenge is created, so editing a group never changes who may sign an in-flight payment.
  • Escalation — a payment still pending after N seconds notifies a wider group. It adds approvers; it never lowers the quorum or extends the expiry.

What this breaks, and what it does not

Broken: the after-the-fact parameter swap

The classic invoice-redirection attack changes the beneficiary between review and submission. Here that is structurally impossible to hide: the signature covers the IBAN and the amount, so a changed beneficiary means the receipt no longer verifies and your own code refuses to send. Broken alongside it: replaying a session-scoped entitlement, spending an old approval on a new payment, and a compromised service quietly approving on its own behalf.

Not broken: a convincing invoice

If a fraudulent invoice reaches your finance team and someone approves it having read it carefully, they have signed the fraud. The gate proves a named person decided over exact details; it does not know the vendor is fake. Keep supplier verification and callback procedures — this composes with them, and makes the record of who decided unambiguous afterwards.

The evidence it leaves

A receipt, not a log line

Each approval produces a signed receipt over the canonical payload — the beneficiary, the amount, the currency, the nonce, the time.

Verifiable without us

An auditor recomputes the payload from your records and checks the signature against the approver's public key using @intyga/verify. No INTYGA secret, no API call.

Tamper-evident ordering

Approvals, denials and rule changes append to a hash-linked ledger with periodic checkpoints, so committed entries cannot be quietly rewritten later.

Stated precisely: the log proves that committed entries were not altered. It cannot prove that an event was never withheld from commitment. Evidence retention on the Business tier is 3-year, with auditor export.

Where this fits your obligations

If you operate under DORA, the same receipts are the evidence its change management and authentication requirements ask for — a per-action, parameter-bound record of who authorized what, verifiable outside the platform that produced it. Nobody can sell you conformity with a regulation; what this gives you is the artefact an assessor asks to see. See the DORA mapping, or read “Signing the Letter, Not the Envelope” for the underlying argument.

This pattern extends beyond payments

Anywhere a machine performs an irreversible action on behalf of a person, the same architecture applies: AI agents issuing payouts or refunds, privileged operations on the systems of record, deployments that change how payments are computed, and releases of the software that moves the money.

Start on the payment path that would hurt most.

One transfer type, one approval rule. When the flow works, widen it. Approvals are not metered, so the Developer tier gates as many transfers as you make, at no cost — what the paid tiers add is seats, a longer evidence-retention window, and quorum rules.

See the DORA mapping →