> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.crisscross.money/api-reference/collect/payments/manage-payments/cancel-session/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.crisscross.money/_mcp/server. # Cancel a Session POST https://api.crisscross.money/v1/checkout/session/cancel Content-Type: application/json Cancels a checkout session. The session is locked against any further payment attempts, and every in-flight (non-terminal) transaction linked to the session is cancelled in the same call. Transactions that have already reached a terminal state (settled, failed, refunded) are not affected. If a transaction on the session is being processed by a provider that does not support cancelling an in-progress transaction, the session enters the **`PENDING_CANCELLATION`** state instead of `CANCELLED`. While in this state, no further payment attempts can be initiated against the session, but the session is not considered fully cancelled until the in-flight transaction reaches a terminal state on its own. The response will list the unstoppable transactions in `pendingTransactionIds`. Reference: https://docs.crisscross.money/api-reference/collect/payments/manage-payments/cancel-session ## Request ### Body (application/json) This endpoint expects a CancelSessionRequest. - `sessionId` (string, required) — Identifier of the checkout session to cancel. UUID v7. - `reason` (string, optional) — Optional free-form reason for the cancellation, recorded for reporting. ## Response ### 200 Session cancellation accepted. - `sessionId` (string, required) — Identifier of the session that was cancelled. UUID v7. - `status` (enum, required) — The resulting state of the session. `CANCELLED` indicates the session and all of its in-flight transactions have been cancelled. `PENDING_CANCELLATION` indicates the session is locked against further payment attempts, but at least one in-flight transaction belongs to a provider that does not support in-process cancellation — the session will move to `CANCELLED` once those transactions reach a terminal state. - Allowed values: `CANCELLED`, `PENDING_CANCELLATION` - `message` (string, required) — Human-readable status message. - `cancelledTransactionIds` (list of string, required) — Transactions on the session that were cancelled as part of this call. UUID v7. - `pendingTransactionIds` (list of string, required) — Transactions still in flight that could not be cancelled because their provider does not support in-process cancellation. Populated only when `status` is `PENDING_CANCELLATION`. Each entry will progress to its own terminal state independently. UUID v7. ## Errors ### 400 Bad Request Error Invalid request parameters. - `message` (string, required) — Human-readable description of what went wrong. - `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on. - `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth. ### 401 Unauthorized Error Unauthorized. - `message` (string, required) — Human-readable description of what went wrong. - `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on. - `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth. ### 404 Not Found Error Session not found. - `message` (string, required) — Human-readable description of what went wrong. - `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on. - `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth. ### 409 Conflict Error Conflict — the session is already cancelled or in a terminal state and cannot be cancelled again. - `message` (string, required) — Human-readable description of what went wrong. - `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on. - `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth. ### 422 Unprocessable Entity Error Unprocessable Entity. - `message` (string, required) — Human-readable description of what went wrong. - `error` (string, required) — Error category. Most endpoints return the HTTP reason phrase (e.g. "Bad Request", "Unauthorized", "Conflict", "Unprocessable Entity", "Internal Server Error"). Some return a machine-readable snake_case code instead — notably the FX and rate-lock rejections on checkout session creation and payment initiation, where several distinct codes share a single HTTP status. Where a code is present it is the finer discriminator and is safe to branch on. - `statusCode` (integer, required) — HTTP status code, mirroring the response header. The response header is the source of truth. ## Examples ### Session fully cancelled **Request** ```json { "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" } ``` **Response** ```json { "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "status": "CANCELLED", "message": "Session cancelled. 1 in-flight transaction was cancelled as part of this call.", "cancelledTransactionIds": [ "01951c8a-8b4c-7d2a-8f1e-5d6c7b8a9e10" ], "pendingTransactionIds": [] } ``` **SDK Code** ```python Session fully cancelled import requests url = "https://api.crisscross.money/v1/checkout/session/cancel" payload = { "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Session fully cancelled const url = 'https://api.crisscross.money/v1/checkout/session/cancel'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"sessionId":"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70","reason":"Customer abandoned checkout"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Session fully cancelled package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.crisscross.money/v1/checkout/session/cancel" payload := strings.NewReader("{\n \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\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(res) fmt.Println(string(body)) } ``` ```ruby Session fully cancelled require 'uri' require 'net/http' url = URI("https://api.crisscross.money/v1/checkout/session/cancel") 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 \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\n}" response = http.request(request) puts response.read_body ``` ```java Session fully cancelled import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.crisscross.money/v1/checkout/session/cancel") .header("Content-Type", "application/json") .body("{\n \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\n}") .asString(); ``` ```php Session fully cancelled request('POST', 'https://api.crisscross.money/v1/checkout/session/cancel', [ 'body' => '{ "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" }', 'headers' => [ 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Session fully cancelled using RestSharp; var client = new RestClient("https://api.crisscross.money/v1/checkout/session/cancel"); var request = new RestRequest(Method.POST); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Session fully cancelled import Foundation let headers = ["Content-Type": "application/json"] let parameters = [ "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.crisscross.money/v1/checkout/session/cancel")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Session locked, waiting on an unstoppable transaction **Request** ```json { "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" } ``` **Response** ```json { "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "status": "PENDING_CANCELLATION", "message": "Session locked. Awaiting terminal state on 1 in-flight transaction whose provider does not support in-process cancellation.", "cancelledTransactionIds": [ "01951c8a-8b4c-7d2a-8f1e-5d6c7b8a9e10" ], "pendingTransactionIds": [ "01951c8a-ad6f-7b1c-a2d3-7f8e9d0c1b32" ] } ``` **SDK Code** ```python Session locked, waiting on an unstoppable transaction import requests url = "https://api.crisscross.money/v1/checkout/session/cancel" payload = { "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" } headers = {"Content-Type": "application/json"} response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Session locked, waiting on an unstoppable transaction const url = 'https://api.crisscross.money/v1/checkout/session/cancel'; const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"sessionId":"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70","reason":"Customer abandoned checkout"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Session locked, waiting on an unstoppable transaction package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.crisscross.money/v1/checkout/session/cancel" payload := strings.NewReader("{\n \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\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(res) fmt.Println(string(body)) } ``` ```ruby Session locked, waiting on an unstoppable transaction require 'uri' require 'net/http' url = URI("https://api.crisscross.money/v1/checkout/session/cancel") 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 \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\n}" response = http.request(request) puts response.read_body ``` ```java Session locked, waiting on an unstoppable transaction import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.crisscross.money/v1/checkout/session/cancel") .header("Content-Type", "application/json") .body("{\n \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\n}") .asString(); ``` ```php Session locked, waiting on an unstoppable transaction request('POST', 'https://api.crisscross.money/v1/checkout/session/cancel', [ 'body' => '{ "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" }', 'headers' => [ 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Session locked, waiting on an unstoppable transaction using RestSharp; var client = new RestClient("https://api.crisscross.money/v1/checkout/session/cancel"); var request = new RestRequest(Method.POST); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"sessionId\": \"01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70\",\n \"reason\": \"Customer abandoned checkout\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Session locked, waiting on an unstoppable transaction import Foundation let headers = ["Content-Type": "application/json"] let parameters = [ "sessionId": "01951c8a-7c3d-7e1f-9d4a-2b3c4d5e6f70", "reason": "Customer abandoned checkout" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.crisscross.money/v1/checkout/session/cancel")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ```