curl --request POST \
--url https://api.pgw-sandbox.finventi.com/v3/payments/cancellations \
--header 'Content-Type: application/json' \
--data '
{
"paymentIds": [
123
],
"additionalComment": "<string>"
}
'import requests
url = "https://api.pgw-sandbox.finventi.com/v3/payments/cancellations"
payload = {
"paymentIds": [123],
"additionalComment": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({paymentIds: [123], additionalComment: '<string>'})
};
fetch('https://api.pgw-sandbox.finventi.com/v3/payments/cancellations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.pgw-sandbox.finventi.com/v3/payments/cancellations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'paymentIds' => [
123
],
'additionalComment' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.pgw-sandbox.finventi.com/v3/payments/cancellations"
payload := strings.NewReader("{\n \"paymentIds\": [\n 123\n ],\n \"additionalComment\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pgw-sandbox.finventi.com/v3/payments/cancellations")
.header("Content-Type", "application/json")
.body("{\n \"paymentIds\": [\n 123\n ],\n \"additionalComment\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pgw-sandbox.finventi.com/v3/payments/cancellations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"paymentIds\": [\n 123\n ],\n \"additionalComment\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"results": [
{
"paymentId": 123,
"status": "ACCEPTED",
"error": "<string>"
}
]
}{
"code": "<string>",
"data": {
"errors": [
{
"code": "<string>",
"field_name": "<string>",
"message": "<string>",
"value": "<string>"
}
],
"message": "<string>"
},
"message": "<string>"
}{
"code": "<string>",
"data": {
"errors": [
{
"code": "<string>",
"field_name": "<string>",
"message": "<string>",
"value": "<string>"
}
],
"message": "<string>"
},
"message": "<string>"
}{
"code": "<string>",
"data": {
"errors": [
{
"code": "<string>",
"field_name": "<string>",
"message": "<string>",
"value": "<string>"
}
],
"message": "<string>"
},
"message": "<string>"
}Cancel payments
Requests cancellation of one or more outbound SEPA, SEPA Direct Debit, SWIFT or T2 payments.
curl --request POST \
--url https://api.pgw-sandbox.finventi.com/v3/payments/cancellations \
--header 'Content-Type: application/json' \
--data '
{
"paymentIds": [
123
],
"additionalComment": "<string>"
}
'import requests
url = "https://api.pgw-sandbox.finventi.com/v3/payments/cancellations"
payload = {
"paymentIds": [123],
"additionalComment": "<string>"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({paymentIds: [123], additionalComment: '<string>'})
};
fetch('https://api.pgw-sandbox.finventi.com/v3/payments/cancellations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.pgw-sandbox.finventi.com/v3/payments/cancellations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'paymentIds' => [
123
],
'additionalComment' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.pgw-sandbox.finventi.com/v3/payments/cancellations"
payload := strings.NewReader("{\n \"paymentIds\": [\n 123\n ],\n \"additionalComment\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pgw-sandbox.finventi.com/v3/payments/cancellations")
.header("Content-Type", "application/json")
.body("{\n \"paymentIds\": [\n 123\n ],\n \"additionalComment\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pgw-sandbox.finventi.com/v3/payments/cancellations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"paymentIds\": [\n 123\n ],\n \"additionalComment\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"results": [
{
"paymentId": 123,
"status": "ACCEPTED",
"error": "<string>"
}
]
}{
"code": "<string>",
"data": {
"errors": [
{
"code": "<string>",
"field_name": "<string>",
"message": "<string>",
"value": "<string>"
}
],
"message": "<string>"
},
"message": "<string>"
}{
"code": "<string>",
"data": {
"errors": [
{
"code": "<string>",
"field_name": "<string>",
"message": "<string>",
"value": "<string>"
}
],
"message": "<string>"
},
"message": "<string>"
}{
"code": "<string>",
"data": {
"errors": [
{
"code": "<string>",
"field_name": "<string>",
"message": "<string>",
"value": "<string>"
}
],
"message": "<string>"
},
"message": "<string>"
}Overview
Cancels up to 100 payments in one request. It is the only way to cancel a SWIFT or T2 payment. The older per-scheme endpoints remain available for existing integrations: Cancel SEPA transaction (v1) and Cancel SDD payment (v0). You do not pass the schema and it is not returned. The platform derives it from the payments you reference -SEPA, SDD, SWIFT or T2, where SEPA covers both SEPA CT and Instant - and names
it only when it rejects your reason or additionalComment.
reason and paymentIds are required. additionalComment is optional, 1-105 characters.
400 MIXED_CANCELLATION_SCHEMAS.Reason codes
reason must be valid for the derived schema. The enum in the schema below is the union across all
schemas, so it accepts values your request will reject.
| Schema | Accepted reasons |
|---|---|
SEPA | AC03 AM09 CUST DUPL FRAD TECH |
SWIFT | AGNT AM09 COVR CURR CUST CUTA DUPL FRAD TECH UPAY |
T2 | AGNT AM09 CUST CUTA DT01 DUPL FRAD NARR TECH UPAY |
SDD outbound | AC01 AC04 AC06 AC13 AG01 AG02 AM04 AM05 BE05 CNOR DNOR DT01 ED05 FF01 MD01 MD02 MD07 MS02 MS03 PY01 RC01 RR01 RR02 RR03 RR04 SL01 |
SDD inbound | AGNT CURR CUST CUTA DUPL FRAD TECH UPAY |
SDD accepts both sets at request level and enforces the direction per payment. A reason valid for
the other direction is rejected in results.
When you can send additionalComment
| Schema | Rule |
|---|---|
SEPA | Only with AC03, AM09, CUST or FRAD |
SWIFT | Optional with any reason, never required |
T2 | Any reason, and required with NARR |
SDD | Not permitted |
Reading the response
You get200 OK whenever the request itself was well formed - including when every payment in it was
rejected. Check results, not the status code.
{
"results": [
{ "paymentId": 12345, "status": "ACCEPTED" },
{
"paymentId": 12346,
"status": "REJECTED",
"error": "Cancellation is not supported for correspondent DEUTDEFFXXX (transaction 12346)"
}
]
}
results, sorted by id. A payment rejected on its own merits
does not affect the others.
error is a human-readable message, not a stable code. Log it and show it to your operators, but
do not branch on its text.Errors
A 4xx response means nothing was cancelled.| Status | Code | Cause |
|---|---|---|
| 400 | ILLEGAL_CANCELLATION_REASON | Reason not valid for the derived schema |
| 400 | VALIDATION_ERROR | additionalComment sent where not allowed, or missing with NARR on T2 |
| 400 | MIXED_CANCELLATION_SCHEMAS | Payments resolve to more than one schema |
| 400 | UNSUPPORTED_CANCELLATION_SCHEMA | Payment type has no cancellation path |
| 404 | TRANSACTION_NOT_FOUND | Unknown payment id |
What happens to an accepted payment
ACCEPTED means different things depending on whether the payment had already left the platform.
- Not yet sent
- Already sent
Created, To sign or Signed is cancelled outright. Its status becomes
Cancelled, nothing is sent to the beneficiary bank, and no cancellation request is created - so
you receive a payment-status-change webhook, not a payment-cancellation-status-change one.This is final. Nobody can refuse it.Sent to clear, Accepted or Completed gets a cancellation request created and a
cancellation message sent to the beneficiary or correspondent bank.ACCEPTED here means the request was sent, not that the money is coming back. The other bank
decides. Track the outcome through the
cancellation webhook until it reaches
PAYMENT_RETURNED, CANCELLATION_REFUSED or CANCELLATION_REJECTED.SDD payments are the exception. They are cancellable only in Accepted and always
create a cancellation request.Extra conditions for SWIFT
A SWIFT payment that has already been sent is checked against the correspondent bank first. It is rejected when the payment has no UETR, when the debtor has no BIC to send the camt.056 from, when the correspondent BIC cannot be resolved from the payment, or when the correspondent does not support cancellation.Retrying
This endpoint takes noIdempotency-Key and does not need one. A payment whose cancellation is
already in flight comes back as REJECTED, so you can replay a whole batch after a partial failure
and only the payments that did not go through will be processed.
Body
Ids of the payments to cancel. All must resolve to the same cancellation schema.
1 - 100 elementsCancellation reason. The set of accepted values depends on the schema derived from the referenced payments — the enum below is the union across all schemas, not the set valid for any one request:
- SEPA CT / INST: DUPL, CUST, FRAD, TECH, AC03, AM09
- SEPA DD outbound: AC01, AC04, AC06, AC13, AG01, AG02, AM04, AM05, CNOR, DNOR, MD01, MD02, MD07, MS02, MS03, RC01, RR01, RR02, RR03, RR04, SL01, BE05, FF01, DT01, ED05, PY01
- SEPA DD inbound: DUPL, AGNT, CURR, CUST, CUTA, UPAY, TECH, FRAD
- SWIFT: DUPL, CUST, FRAD, TECH, AM09, AGNT, COVR, CURR, CUTA, UPAY
- T2: DT01, DUPL, CUTA, TECH, UPAY, CUST, AGNT, FRAD, AM09, NARR
AC01, AC03, AC04, AC06, AC13, AG01, AG02, AGNT, AM04, AM05, AM09, BE05, CNOR, COVR, CURR, CUST, CUTA, DNOR, DT01, DUPL, ED05, FF01, FRAD, MD01, MD02, MD07, MS02, MS03, NARR, PY01, RC01, RR01, RR02, RR03, RR04, SL01, TECH, UPAY Free-text detail accompanying the reason. Permitted only where the derived schema allows it:
- SEPA CT / INST: with CUST, FRAD, AM09 or AC03
- SEPA DD: not permitted
- SWIFT: optional with any reason
- T2: with any reason, and required when the reason is NARR
1 - 105Response
Request accepted. Every referenced payment carries its own outcome in results; a payment rejected on its own merits does not fail the request. Returned even when every payment was rejected — always inspect results rather than relying on the status code.
Per-payment outcome, one entry for every requested payment id, ordered by payment id
Show child attributes
Show child attributes