Update recurring payment
Updates an SDP-custody recurring payment.
PATCH/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 · exampleprp_exampleSDP recurring payment record ID.
Headers
-
x-project-id·string· min length 1 · exampleprj_exampleRequired for dashboard sessions. API keys use their assigned project.
-
Idempotency-Key·string· requiredUse 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 returns422. 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 · examplecwlt_exampleOptional exact replacement SDP Wallet ID (
idfromGET /v1/wallets). ProviderwalletId, wallet addresses, and the legacysourceWalletIdselector are not accepted. Active replacements require write access to both the old and new source wallets. -
counterpartyId·string· min length 1 · examplecpty_exampleOptional replacement counterparty. When provided, counterpartyAccountId is also required.
-
counterpartyAccountId·string· min length 1 · examplecpacct_exampleOptional replacement counterparty account. Without counterpartyId, this changes the account for the current counterparty.
-
token·string· exampleEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1vOptional replacement token. Must be a USD stablecoin (e.g. USDC) or a token issued in this project; native SOL is not supported.
-
amount·string· example25.00Optional replacement amount in UI units.
-
periodHours·integer· greater than 0 · max 8760 · example720Optional replacement billing period length in hours. Active term changes create a replacement on-chain subscription.
-
firstCollectionAt·string | null· formatdate-time· example2099-01-01T00:00:00.000ZPending-only first collection timestamp. Use null to clear the pending first collection override.
-
nextCollectionDueAt·string | null· formatdate-time· example2099-02-01T00:00:00.000ZActive-only next due timestamp. Use null to reset to the earliest eligible collection time.
-
metadataUri·string | null· formaturi· max length 128 · examplehttps://example.com/subscriptions/monthly-usdc.jsonOptional plan metadata URI. Use null to clear it. Active metadata-only edits update the existing on-chain plan in place.
Responses
-
200Recurring payment updated-
data·object· requiredRecurring payment response payload.
-
recurringPayment·object· requiredSDP-custody outbound recurring payment record.
-
id·string· required · exampleprp_exampleSDP recurring payment ID.
-
organizationId·string· required · min length 1 · exampleorg_exampleOrganization identifier.
-
projectId·string· required · min length 1 · exampleprj_exampleProject identifier.
-
sourceCustodyWalletId·string | null· required · examplecwlt_exampleExact source SDP Wallet ID, or null for unresolved legacy records.
-
sourceProviderWalletId·string· required · min length 1 · exampleprivy_wallet_123Source Provider wallet ID retained as evidence.
-
sourceAddress·string· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Source wallet address.
-
counterpartyId·string· required · examplecpty_exampleCounterparty ID.
-
counterpartyAccountId·string· required · examplecpacct_exampleCounterparty account ID.
-
destinationAddress·string· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Counterparty wallet owner address.
-
destinationTokenAccount·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112Derived counterparty token account used for collection.
-
token·string· requiredSPL token mint address.
-
amount·string· required · example100.00Token amount in UI units (decimal string).
-
periodHours·integer· required · greater than 0 · example720 -
firstCollectionAt·string | null· required · formatdate-time· example2025-01-01T00:00:00.000ZRequested first collection timestamp.
-
nextCollectionDueAt·string | null· required · formatdate-time· example2025-01-01T00:00:00.000ZNext due collection timestamp.
-
planId·string | null· requiredLinked SDP subscription plan ID.
-
subscriptionId·string | null· requiredLinked SDP subscription ID.
-
planPda·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112On-chain plan PDA.
-
planCreatedAt·string | null· requiredOn-chain plan createdAt value as an unsigned integer string.
-
planCreationSignature·string | null· requiredSolana signature for plan creation.
-
subscriptionPda·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112On-chain subscription PDA.
-
subscriptionAuthorityAddress·string | null· required · min length 32 · max length 44 · exampleSo11111111111111111111111111111111111111112On-chain subscription authority address.
-
authorizationSignature·string | null· requiredSolana signature for subscription authorization.
-
status·string· required · one ofpending_activation,activating,active,updating,canceling,resuming,paused,canceled,expired· exampleactiveRecurring payment status.
-
pausedReason·string | null· required · one ofcustody_policy_refused,failed_attempts· examplenullWhy SDP paused the recurring payment, set exactly while
statusispaused:custody_policy_refusedwhen the custody provider's policy refused to sign a collection,failed_attemptswhen one due period failed 5 times. Resume clears it. -
metadataUri·string | null· requiredOptional plan metadata URI.
-
createdBy·string | null· requiredCreator user ID or API key ID.
-
createdAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
updatedAt·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
tokenApproval·objectPresent 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 ofrevoked,kept,revoking· examplekeptrevokedonce the approval has been revoked since the cancel.keptwhile other live recurring payments on the same wallet and token, in this or another project, still use it;sharedWithlists the ones in this project. It is revoked once the last of them ends.revokingwhen 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 · exampleSo11111111111111111111111111111111111111112The approval's delegate: the Subscriptions program's authority for this wallet and token.
-
sharedWith·object[]· requiredThe other live recurring payments in this project on the same wallet and token that keep the approval. Empty unless
statusiskept, and empty then when only recurring payments in other projects keep it.-
id·string· required · exampleprp_exampleSDP recurring payment ID.
-
status·string· required · one ofpending_activation,activating,active,updating,canceling,resuming,paused,canceled,expired· exampleactiveRecurring payment status.
-
-
-
-
meta·object· requiredResponse metadata.
-
requestId·string· required · min length 1 · examplereq_exampleRequest identifier for tracing.
-
timestamp·string· required · formatdate-time· example2025-01-01T00:00:00.000ZISO 8601 timestamp.
-
-
-
400Bad Request-
error·object· requiredStandard error payload.
-
code·string· requiredMachine-readable error code.
-
message·string· requiredHuman-readable error message.
-
details·objectOptional error details for debugging.
*·any
-
-
meta·object-
requestId·string· min length 1 · examplereq_exampleRequest identifier for tracing.
-
-
-
401UnauthorizedSame fields as
400. -
403Forbidden. Requires therecurring.executeaction: an API key must hold it (a module'smanageorexecutealso covers itsread), and a signed-in member's organization role must include it. Without it the error is INSUFFICIENT_PERMISSIONS anderror.details.actionnames the action. Any other 403 names its own reason.Same fields as
400. -
404Not FoundSame fields as
400. -
409ConflictSame fields as
400. -
422Unprocessable EntitySame fields as
400. -
500Internal Server ErrorSame 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.