API Guide
Log In
API Guide

Preventing duplicate payments

Learn about how to mitigate the risk of duplicate payments

Crezco includes built-in protection against duplicate payments in 2 key ways:

  1. Duplicate detection using the partnerEntityId. This is triggered:
    1. When a pay run is created
    2. When a pay run is checked out
  2. Duplicate detection warning during the payment flow

This guide explains how each check works and what your integration needs to do to ensure duplicates are caught reliably.


Duplicate detection using partnerEntityId

About partnerEntityIds

To protect from potential duplicate payments Crezco requires our partners to submit a partnerEntityId at both the pay run and payable levels. The partnerEntityId should have a 1:1 relationship with an item that needs paying in your system. We will then use this to make sure the same item can never be paid twice.

Duplicate partnerEntityId checks

When you create a pay run, both the pay run and its constituent payables are in draft mode. In this state, partnerEntityId values can be freely re-used across pay runs and payables, as a draft pay run may be discarded and recreated to correct issues before payment is initiated.

Once the Checkout endpoint is called for a pay run, it leaves draft mode. At this point:

  • The pay run's partnerEntityId is permanently locked and cannot be re-used for any future pay run
  • Each payable's partnerEntityId cannot be re-used in a different pay run unless the payable is in a terminal failure state (Cancelled or Reversed)

This duplicate check runs at two points: when creating a pay run and when checking out a pay run. In both cases, if a payable's partnerEntityId is already in use on a payable that is not in a Draft, Cancelled, or Reversed state, the request is rejected with a 409 Conflict.

For example:

  1. You create Pay Run A and Pay Run B, each containing a payable with the same partnerEntityId. Both are in Draft status, so this is permitted.
  2. You check out Pay Run A. Its payable moves to Open status.
  3. You attempt to check out Pay Run B. The request is rejected with a 409 Conflict because a payable with the same partnerEntityId is already Open.
  4. You attempt to create Pay Run C with the same payable partnerEntityId. This is also rejected with a 409 Conflict for the same reason.
  5. You cancel Pay Run A. Its payable moves to Cancelled status.
  6. You can now check out Pay Run B or create Pay Run C (not both), as the payable's partnerEntityId is no longer in use on an active payable.

If one or more duplicate partnerEntityId are present, the API returns a 409 Conflict with details of the conflicting IDs.

{
    "type": "https://api.crezco.com/api/errors/conflict",
    "title": "Conflict",
    "status": 409,
    "detail": "One or more validation errors occurred.",
    "errors": {
        "PartnerEntityId": [
            "Duplicate PartnerEntityId found for pay-run-1"
        ],
        "Payables[0].PartnerEntityId": [
            "Duplicate PartnerEntityId found for payable-1 on pay run fff1d8ac-d5db-428c-9ae2-9f6d14eadbd2"
        ]
    }
}

Note: This check returns a 409, not a 400. Treat this response as a signal that the pay run or payable has already been submitted and should not be resubmitted.

Re-using partnerEntityId for multiple payables

In most cases, a partnerEntityId should only ever be paid once. However, there are legitimate scenarios where you may need to resubmit a payment for the same item, for example if a previous payment failed or was cancelled.

In these cases, re-using the partnerEntityId is permitted, provided the original payable has reached a terminal failure state. The following states allow re-use:

  • Draft (this is re-usable as the payable is still in draft - see above)
  • Cancelled
  • Reversed

If the original payable is in any other state, the request will be rejected with a 409 Conflict as described above.

⚠️

Note: You should only re-use a partnerEntityId when you have confirmed via the API or a webhook that the original payable has reached one of these terminal states. Do not rely on a timeout or assume failure. Always verify the state explicitly before resubmitting.


Duplicate detection warning during the payment flow

Once checkout is initiated for a pay run, Crezco runs additional checks within the hosted payment flow.

If a payable in the pay run closely matches a recent payment, a warning is shown to the user before they authorise. The user can choose to proceed.

Conditions for this check to apply

The warning fires when all four of the following fields match a payable from a different pay run:

  • Reference
  • Beneficiary account (sort code and account number, or IBAN)
  • Amount
  • Funding currency

The match is only checked against non-failed payables within the past 24 hours, excluding fee-payables

All four fields must match (a shared reference alone is not sufficient). This check runs across all payment sources for the organisation. A bank payment and a card payment with identical payable details would both be caught.

⚠️

Note: Even if two separate completely different pay runs are paid within this time period, but two of the individual payables match all of these rules, the duplicate payment screen will still appear.


Image of duplicate warning


Your responsibilities as a partner

The duplicate detection checks rely on your integration mapping each bill or payment to a stable, consistent partnerEntityId across submission attempts.

If your system generates a new partnerEntityId each time the same bill is submitted for payment, duplicate detection will not work correctly.

The recommended approach is to derive partnerEntityId from a stable identifier in your system, such as the bill or invoice ID, and reuse it consistently across any retries or resubmissions.

Some examples:

Accounting software

  • In your accounting software, the partnerEntityId could be the bill to be paid e.g. a payable partnerEntityId of INV-001
  • Alternatively it could be the part of the bill to be paid if you allow partial bill payments
    • e.g. a payable partnerEntityId of INV-001-part-1
⚠️

We recommend that each part payment is recorded in partner system with its own ID, and partner system has to ensure that only the part payments get sent to us

Payroll software

  • In your payroll software, partnerEntityId could be the the payslip id (e.g. jane-doe-april-2026-payslip)

Displaying payments as available to pay in your platform

When deciding whether to display a payment as available to pay in your platform, use the state of its corresponding Crezco payable to determine the correct behaviour.

Payable not yet created in Crezco

If no payable has been created yet, call POST .../pay-runs to create one in Draft state, then immediately call the POST .../Checkout endpoint and redirect the user to the returned URL.

Payable exists in Crezco

Use the following table to determine what to show:


Payable stateDisplay as available to pay?Action
DraftYesCall Checkout and redirect the user to the returned URL
OpenYesCall Checkout and redirect the user to the returned URL
UserInitiationInProgressYesCall Checkout and redirect the user to the returned URL
CancelledYesCreate a new pay run with the same payable partnerEntityId, then call Checkout and redirect the user
ReversedYesCreate a new pay run with the same payable partnerEntityId, then call Checkout and redirect the user
AwaitingMultiAuthNoDisable the pay button / Do not display the payable as available to pay
BankInitiatingNoDisable the pay button / Do not display the payable as available to pay
TransfersInProgressNoDisable the pay button / Do not display the payable as available to pay
ProcessingNoDisable the pay button / Do not display the payable as available to pay
WaitingForCustomerNoDisable the pay button / Do not display the payable as available to pay
CancellingNoDisable the pay button / Do not display the payable as available to pay
ReversingNoDisable the pay button / Do not display the payable as available to pay
ProcessedNoThe payment has completed successfully. Do not display as available to pay
ℹ️

Note: Always re-call the Checkout endpoint when redirecting a user to the payment flow. Do not cache the URL returned from a previous Checkout call, as the state of the pay run may have changed and the URL may no longer be valid.