Update recurring payment

Updates an SDP-custody recurring payment.

PATCH/v1/payments/recurring-payments/{id}Run in dashboard

Pending records are updated directly. Active metadata and due-date edits are applied in place, while active term, source, destination, or token edits create a replacement Solana subscription, authorize it, cancel the old subscription, and then swap the recurring payment to the replacement records. Requires an Idempotency-Key, because an edit can sign and broadcast a replacement subscription. A same-key retry of a completed edit replays its answer. A retry of an interrupted edit gets 409 IDEMPOTENCY_KEY_IN_FLIGHT with Retry-After until the attempt is stale, then resumes from the last step SDP recorded. If a step was sent but not yet recorded when the edit was interrupted, which is rare, the retry can fail or send that step again, so read the recurring payment with GET before editing it again.

Requires an API key with the recurring.execute action.

Path parameters

  • id · string · required · min length 1 · example prp_example

    SDP recurring payment record ID.

Headers

  • x-project-id · string · min length 1 · example prj_example

    Required for dashboard sessions. API keys use their assigned project.

  • Idempotency-Key · string · required

    Use a unique 1–255 printable ASCII key per operation. Reuse it with the same request on retries. An in-flight attempt returns 409; a changed request returns 422. Retry and recovery rules.

Request body

application/json · required

Updates a recurring payment. Pending payments are edited directly. Active metadata or due-date updates are applied in place, while active term/source/destination/token updates create and authorize a replacement subscription before canceling the old one.

  • sourceCustodyWalletId · string · min length 1 · example cwlt_example

    Optional exact replacement SDP Wallet ID (id from GET /v1/wallets). Provider walletId, wallet addresses, and the legacy sourceWalletId selector are not accepted. Active replacements require write access to both the old and new source wallets.

  • counterpartyId · string · min length 1 · example cpty_example

    Optional replacement counterparty. When provided, counterpartyAccountId is also required.

  • counterpartyAccountId · string · min length 1 · example cpacct_example

    Optional replacement counterparty account. Without counterpartyId, this changes the account for the current counterparty.

  • token · string · example EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

    Optional replacement token. Must be a USD stablecoin (e.g. USDC) or a token issued in this project; native SOL is not supported.

  • amount · string · example 25.00

    Optional replacement amount in UI units.

  • periodHours · integer · greater than 0 · max 8760 · example 720

    Optional replacement billing period length in hours. Active term changes create a replacement on-chain subscription.

  • firstCollectionAt · string | null · format date-time · example 2099-01-01T00:00:00.000Z

    Pending-only first collection timestamp. Use null to clear the pending first collection override.

  • nextCollectionDueAt · string | null · format date-time · example 2099-02-01T00:00:00.000Z

    Active-only next due timestamp. Use null to reset to the earliest eligible collection time.

  • metadataUri · string | null · format uri · max length 128 · example https://example.com/subscriptions/monthly-usdc.json

    Optional plan metadata URI. Use null to clear it. Active metadata-only edits update the existing on-chain plan in place.

Responses

  • 200 Recurring payment updated

    • data · object · required

      Recurring payment response payload.

      • recurringPayment · object · required

        SDP-custody outbound recurring payment record.

        • id · string · required · example prp_example

          SDP recurring payment ID.

        • organizationId · string · required · min length 1 · example org_example

          Organization identifier.

        • projectId · string · required · min length 1 · example prj_example

          Project identifier.

        • sourceCustodyWalletId · string | null · required · example cwlt_example

          Exact source SDP Wallet ID, or null for unresolved legacy records.

        • sourceProviderWalletId · string · required · min length 1 · example privy_wallet_123

          Source Provider wallet ID retained as evidence.

        • sourceAddress · string · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          Source wallet address.

        • counterpartyId · string · required · example cpty_example

          Counterparty ID.

        • counterpartyAccountId · string · required · example cpacct_example

          Counterparty account ID.

        • destinationAddress · string · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          Counterparty wallet owner address.

        • destinationTokenAccount · string | null · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          Derived counterparty token account used for collection.

        • token · string · required

          SPL token mint address.

        • amount · string · required · example 100.00

          Token amount in UI units (decimal string).

        • periodHours · integer · required · greater than 0 · example 720

        • firstCollectionAt · string | null · required · format date-time · example 2025-01-01T00:00:00.000Z

          Requested first collection timestamp.

        • nextCollectionDueAt · string | null · required · format date-time · example 2025-01-01T00:00:00.000Z

          Next due collection timestamp.

        • planId · string | null · required

          Linked SDP subscription plan ID.

        • subscriptionId · string | null · required

          Linked SDP subscription ID.

        • planPda · string | null · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          On-chain plan PDA.

        • planCreatedAt · string | null · required

          On-chain plan createdAt value as an unsigned integer string.

        • planCreationSignature · string | null · required

          Solana signature for plan creation.

        • subscriptionPda · string | null · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          On-chain subscription PDA.

        • subscriptionAuthorityAddress · string | null · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          On-chain subscription authority address.

        • authorizationSignature · string | null · required

          Solana signature for subscription authorization.

        • status · string · required · one of pending_activation, activating, active, updating, canceling, resuming, paused, canceled, expired · example active

          Recurring payment status.

        • pausedReason · string | null · required · one of custody_policy_refused, failed_attempts · example null

          Why SDP paused the recurring payment, set exactly while status is paused: custody_policy_refused when the custody provider's policy refused to sign a collection, failed_attempts when one due period failed 5 times. Resume clears it.

        • metadataUri · string | null · required

          Optional plan metadata URI.

        • createdBy · string | null · required

          Creator user ID or API key ID.

        • createdAt · string · required · format date-time · example 2025-01-01T00:00:00.000Z

          ISO 8601 timestamp.

        • updatedAt · string · required · format date-time · example 2025-01-01T00:00:00.000Z

          ISO 8601 timestamp.

      • tokenApproval · object

        Present when the recurring payment is canceled, on the cancel response and on get: what became of the token approval it relied on.

        • status · string · required · one of revoked, kept, revoking · example kept

          revoked once the approval has been revoked since the cancel. kept while other live recurring payments on the same wallet and token, in this or another project, still use it; sharedWith lists the ones in this project. It is revoked once the last of them ends. revoking when nothing else uses it but the revoke has not confirmed yet, for example while a collection that was already processing settles; the background sweep finishes it.

        • delegate · string · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          The approval's delegate: the Subscriptions program's authority for this wallet and token.

        • sharedWith · object[] · required

          The other live recurring payments in this project on the same wallet and token that keep the approval. Empty unless status is kept, and empty then when only recurring payments in other projects keep it.

          • id · string · required · example prp_example

            SDP recurring payment ID.

          • status · string · required · one of pending_activation, activating, active, updating, canceling, resuming, paused, canceled, expired · example active

            Recurring payment status.

    • meta · object · required

      Response metadata.

      • requestId · string · required · min length 1 · example req_example

        Request identifier for tracing.

      • timestamp · string · required · format date-time · example 2025-01-01T00:00:00.000Z

        ISO 8601 timestamp.

  • 400 Bad Request

    • error · object · required

      Standard error payload.

      • code · string · required

        Machine-readable error code.

      • message · string · required

        Human-readable error message.

      • details · object

        Optional error details for debugging.

        • * · any
    • meta · object

      • requestId · string · min length 1 · example req_example

        Request identifier for tracing.

  • 401 Unauthorized

    Same fields as 400.

  • 403 Forbidden. Requires the recurring.execute 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.

    Same fields as 400.

  • 404 Not Found

    Same fields as 400.

  • 409 Conflict

    Same fields as 400.

  • 422 Unprocessable Entity

    Same fields as 400.

  • 500 Internal Server Error

    Same fields as 400.

Example request

curl -X PATCH 'https://api.solana.com/v1/payments/recurring-payments/prp_example' \
  -H "Authorization: Bearer $SDP_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceCustodyWalletId": "cwlt_example",
    "counterpartyId": "cpty_example",
    "counterpartyAccountId": "cpacct_example",
    "token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "amount": "25.00",
    "periodHours": 720,
    "firstCollectionAt": "2099-01-01T00:00:00.000Z",
    "nextCollectionDueAt": "2099-02-01T00:00:00.000Z",
    "metadataUri": "https://example.com/subscriptions/monthly-usdc.json"
  }'

Example responses

{
  "data": {
    "recurringPayment": {
      "id": "prp_example",
      "organizationId": "org_example",
      "projectId": "prj_example",
      "sourceCustodyWalletId": "cwlt_example",
      "sourceProviderWalletId": "privy_wallet_123",
      "sourceAddress": "So11111111111111111111111111111111111111112",
      "counterpartyId": "cpty_example",
      "counterpartyAccountId": "cpacct_example",
      "destinationAddress": "So11111111111111111111111111111111111111112",
      "destinationTokenAccount": "So11111111111111111111111111111111111111112",
      "token": "example",
      "amount": "100.00",
      "periodHours": 720,
      "firstCollectionAt": "2025-01-01T00:00:00.000Z",
      "nextCollectionDueAt": "2025-01-01T00:00:00.000Z",
      "planId": "plan_example",
      "subscriptionId": "subscription_example",
      "planPda": "So11111111111111111111111111111111111111112",
      "planCreatedAt": "example",
      "planCreationSignature": "example",
      "subscriptionPda": "So11111111111111111111111111111111111111112",
      "subscriptionAuthorityAddress": "So11111111111111111111111111111111111111112",
      "authorizationSignature": "example",
      "status": "active",
      "pausedReason": null,
      "metadataUri": "example",
      "createdBy": "example",
      "createdAt": "2025-01-01T00:00:00.000Z",
      "updatedAt": "2025-01-01T00:00:00.000Z"
    },
    "tokenApproval": {
      "status": "kept",
      "delegate": "So11111111111111111111111111111111111111112",
      "sharedWith": [
        {
          "id": "prp_example",
          "status": "active"
        }
      ]
    }
  },
  "meta": {
    "requestId": "req_example",
    "timestamp": "2025-01-01T00:00:00.000Z"
  }
}

Error responses use the standard error shape.

Is this page helpful?