---
title: "Cancel payment request"
method: POST
path: "/v1/payments/requests/{paymentRequestId}/cancel"
tags: ["Payments"]
---

# Cancel payment request

`POST /v1/payments/requests/{paymentRequestId}/cancel`

## Request

- Base URL: `http://localhost:8787`
- URL: `http://localhost:8787/v1/payments/requests/{paymentRequestId}/cancel`
- Auth: HTTP bearer

Cancels an open payment request, so its payment link stops building and sponsoring transactions. Requires `payment_requests.manage`. Canceling a request that is already canceled returns it unchanged. A paid or expired request returns 409, and so does one whose fee-sponsored transaction can still land: retry once that transaction's blockhash expires. A payer who signed before the cancel can still land the payment; SDP records it as paid, with `paidLate` set when it landed after the cancel.

## Path parameters

- `paymentRequestId` string, required: SDP payment request ID.

## Headers

- `x-project-id` string: Required for dashboard sessions. API keys use their assigned project.
- `Idempotency-Key` string: Optional, and honored when sent. 1-255 printable ASCII characters; send a UUID per logical operation and reuse it on every retry. Within 24 hours, a retry with the same key and the same request returns the stored status and body with the response header `Idempotent-Replayed: true`. Keys are scoped to the credential: within 24 hours, the same key with a different request, or from a different credential, returns 422 `IDEMPOTENCY_KEY_REUSED`; after that, the same key from a different credential runs as a new request. A rotated API key is a new credential, so after any rotation read the resource with GET to see whether the original request ran, and never resend a money request under the new credential without checking first. A key whose original request is still running returns 409 `IDEMPOTENCY_KEY_IN_FLIGHT` with `Retry-After`. A 400, 401, 403, 409 or 429 refusal is not stored, so the same key runs again once the cause is fixed. Access is checked before the key on every request, so a refused request never claims the key, and a credential that has since lost access to the named resource gets 403 instead of the stored response. A replay is a snapshot of the original response: read the current state with GET instead of sending the request again. If the original request never recorded its outcome (it crashed), a same-key retry gets 409 `IDEMPOTENCY_KEY_IN_FLIGHT` until 15 minutes plus the route's lease have passed since the original was sent (about 16 minutes, or about 20 for transfer batches), then 409 `IDEMPOTENCY_KEY_OUTCOME_UNKNOWN` with no `Retry-After`, as does every later retry with that key: read the resource with GET to see whether it ran, then send again with a new key. After a 5xx, a retry with the same key runs again and answers from the original record instead of acting twice; that answer also carries `Idempotent-Replayed: true`.

## Response `200`

Payment request canceled

- object: Standard success response envelope.
  - `data` object, required: A payment request. Reads never carry its link token.
    - `id` string, required: SDP payment request ID.
    - `organizationId` string, required: Organization ID.
    - `projectId` string, nullable, required: Project ID.
    - `walletId` string, required: Receiving custody wallet's provider ID.
    - `destinationAddress` string, required: Address the payment is sent to.
    - `token` string, required: Token mint address.
    - `amount` string, required: Token amount in UI units (decimal string).
    - `reference` string, required: Solana Pay reference account that identifies the payment on chain.
    - `status` 'awaiting_payment' | 'paid' | 'canceled' | 'expired', required: Payment request status. `awaiting_payment` is open; `paid` was settled by an on-chain payment; `canceled` and `expired` closed without one.
    - `expiresAt` string, date-time, nullable, required: Expiry, if any.
    - `fulfilledByTransferId` string, nullable, required: The inbound transfer that paid the request.
    - `paidLate` boolean, required: Whether the payment landed after the request expired or was canceled. A payer who signed before then can still pay.
    - `lastCheckedAt` string, date-time, nullable, required: When SDP last read the chain for the payment.
    - `canceledByApiKeyId` string, nullable, required: The API key that canceled the request; null when a member did, or it is not canceled.
    - `canceledByUserId` string, nullable, required: User ID of the member who canceled the request; null when an API key did, or it is not canceled.
    - `canceledAt` string, date-time, nullable, required: When it was canceled.
    - `lifecycle` object[], required: Every status the request entered, oldest first.
      - `status` 'awaiting_payment' | 'paid' | 'canceled' | 'expired', required: Payment request status. `awaiting_payment` is open; `paid` was settled by an on-chain payment; `canceled` and `expired` closed without one.
      - `at` string, date-time, required: ISO 8601 timestamp.
    - `createdBy` string, nullable, required: User ID of the member, or of the API key's creator, who created it.
    - `createdAt` string, date-time, required: ISO 8601 timestamp.
    - `updatedAt` string, date-time, required: ISO 8601 timestamp.
  - `meta` object, required: Response metadata.
    - `requestId` string, required: Request identifier for tracing.
    - `timestamp` string, date-time, required: ISO 8601 timestamp.

## Other responses

- `400`: Error
- `401`: Error
- `403`: Forbidden. Requires the `payment_requests.manage` action: an API key must hold it (a module's `manage` or `execute` also covers its `read`), and a signed-in member's organization role must include it. Without it the error is INSUFFICIENT_PERMISSIONS and `error.details.action` names the action. Any other 403 names its own reason.
- `404`: Error
- `409`: Error
- `422`: Error
- `500`: Error
- `502`: Error

## Changes

- **2026-11-02** `9f0b1d561064`: 8 warning
  - added the new `CUSTODY_POLICY_REFUSED` enum value to the `error/code` response property for the response status `400`
  - added the new `CUSTODY_POLICY_REFUSED` enum value to the `error/code` response property for the response status `401`
  - added the new `CUSTODY_POLICY_REFUSED` enum value to the `error/code` response property for the response status `403`
  - added the new `CUSTODY_POLICY_REFUSED` enum value to the `error/code` response property for the response status `404`
  - …4 more
- **2026-10-30** `f4a0135eca08`: 48 warning
  - added the new `RPC_UNAVAILABLE` enum value to the `error/code` response property for the response status `400`
  - added the new `RPC_UNAVAILABLE` enum value to the `error/code` response property for the response status `401`
  - added the new `RPC_UNAVAILABLE` enum value to the `error/code` response property for the response status `403`
  - added the new `RPC_UNAVAILABLE` enum value to the `error/code` response property for the response status `404`
  - …44 more
- **2026-10-28** `f48ddd70434b`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/solana/apis/solana-developer-platform-api/changes/v1/payments/requests/:paymentRequestId/cancel/post.md)

---

[API](https://skmtc.dev/solana/apis/solana-developer-platform-api.md) - [All operations](https://skmtc.dev/solana/apis/solana-developer-platform-api/llms.txt) - [OpenAPI document](https://skmtc.dev/solana/apis/solana-developer-platform-api/revisions/47682f5c382e?raw)
